在 Rails 中加入 table has_many
Join table for has_many through in Rails
我是编程新手 & rails,有些事情我不完全理解。
我正在使用
创建一个应用程序
product has_many categories
category has_many products
如果我理解正确,我需要创建一个连接 table products_categories,它有一个 product_id
和一个 category_id
。首先,我还需要这个 table 的模型吗?如果是,我想它看起来像这样:
class CategoryProduct < ActiveRecord::Base
belongs_to :category
belongs_to :product
end
和 product.rb 中的其他模型:
class Product < ActiveRecord::Base
has_many :category_products
has_many :categories, through: :category_product
has_attached_file :picture,
styles: { medium: "300x300>", thumb: "100x100>" }
validates_attachment_content_type :picture,
content_type: /\Aimage\/.*\z/
validates :price, presence: { message: "Merci d'indiquer le prix du produit" }
validates :name, presence: { message: "Merci d'indiquer le nom du produit" }
validates :weight, presence: { message: "Merci d'indiquer le poids du produit" }
validates :description, presence: { message: "Merci d'écrire une description du produit " }
end
并在 category.rb
class Category < ActiveRecord::Base
has_many :category_products
has_many :products, through: :category_product
validates :name, presence: { message: "Merci d'indiquer le nom de la catégorie" }
end
现在假设我想创建一个产品,在创建它的同时,从类别列表中为该产品指定尽可能多的类别。
到目前为止,这是我的 Product/new.html.slim 观点:
div class="container marged-top"
div class= "col-xs-12 col-md-offset-3 col-md-5 bigmarge"
div class="panel panel-default"
div class= "panel-heading"
h4 Création Produit
div class= "panel-body"
=simple_form_for @product, html: { multipart: true } do |t|
= t.error_notification
= t.input :name, label: 'Nom'
= t.input :description, label: 'Description', required: true
= t.input :price, label: 'Prix', required: true
= t.input :weight, label: 'Poids', required: true
= t.label :picture
= t.file_field :picture
= t.association :categories, as: :check_boxes
= t.button :submit, value: "Valider", class: "btn-success marge-bas"
这是我的 Product 实例的一个简单表单。我想我现在需要一个 CategoryProduct 的表格吗?
如果我希望用户在创建产品时能够向产品添加他想要的任意多个类别,我该如何更改它?
这是我的 category_product table 的迁移文件:
class CreateTableCategoriesProducts < ActiveRecord::Migration
def change
create_table :categories_products do |t|
t.references :product, index: true
t.references :category, index: true
end
add_foreign_key :categories_products, :categories
add_foreign_key :categories_products, :products
end
end
我用以下迁移文件重命名了之前的 table :
class RenameTableCategoriesProducts < ActiveRecord::Migration
def self.up
rename_table :categories_products, :category_products
end
def self.down
rename_table :category_products, :categories_products
end
end
我在 product/new.html.slim 中的 simple_form 上收到以下错误:
undefined method `klass' for nil:NilClass
代码在这里中断:
= t.association :categories, as: :check_boxes
所以我想我的联想还是不太正确
您还必须为每个模型添加 CategoryProduct:
class Product < ActiveRecord::Base
has_many :category_products
has_many :categories, through: :category_product
使用gemsimple form
非常简单。您所要做的就是添加:
t.association :categories
在产品表单中,并将 :category_ids => []
添加到产品控制器中允许的参数列表中
如果您更喜欢复选框而不是多select列表,您可以
t.association :categories, as: check_boxes
最后一件事,要以人类可读的格式显示类别,您需要在类别模型中定义一个 to_s
方法,即。即:
class Category < ActiveRecord::Base
...
def to_s
name
end
end
在Rails中有两种方式建立多对多关系:
has_and_belongs_to_many
在没有干预模型的情况下建立多对多关系。
class Category < ActiveRecord::Base
has_and_belongs_to_many :products
end
class Product < ActiveRecord::Base
has_and_belongs_to_many :categories
end
如果您知道您不需要存储任何关于关系的附加数据或添加任何附加功能,这是一个不错的选择——这在实践中实际上很少见。它使用更少的内存,因为它不必为了 product.category
.
而实例化额外的模型
使用 has_and_belongs_to_many
时,惯例是连接 table 以两个实体的复数形式命名。
Category + Product = products_categories
顺序似乎并不重要。
has_many 通过:
正如您已经猜到的那样,它使用了中间模型。
class CategoryProduct < ActiveRecord::Base
belongs_to :product
belongs_to :category
end
class Category < ActiveRecord::Base
has_many :category_products
has_many :products, through: :category_products
end
class Product < ActiveRecord::Base
has_many :category_products
has_many :categories, through: :category_products
end
这里的优点是您可以在描述关系的连接 table 中存储和检索附加数据。例如,如果你想存储谁将产品添加到类别 - 或者何时创建关系。
为了Rails能够正确找到ProductCategory class has_many though
命名约定为:
model 1(singular) + model 2(plural)
Product + Category = category_products
这是由于 rails 根据 table 名称推断模型 class 的方式所致。使用 categories_products
将大小写 rails 查找 Category::CategoriesProduct
,因为复数词被解释为模块。然而,这实际上只是一个懒惰的命名约定,通常有一个名词可以更好地描述 A 和 B 之间的关系(例如 Categorization
)。
表单和控制器中的多对多。
正如 IvanSelivanov 已经提到的,SimpleForm 具有用于创建选择、复选框等的辅助方法
但是您可能希望改用 label_method 选项,而不是覆盖模型中的 .to_s
方法。
f.assocation :categories, as: :checkboxes, label_method: :name
覆盖 .to_s
会使调试更加困难,并且在某些情况下会给出令人困惑的测试错误消息。
要将控制器中的参数列入白名单,您可以这样做:
class ProductsController < ApplicationController
def create
@product = Product.new(product_params)
if @product.save
redirect_to @product
else
render :new
end
end
def product_params
params.require(:product)
.permit(:name, :categories_ids, ...)
end
end
我是编程新手 & rails,有些事情我不完全理解。 我正在使用
创建一个应用程序product has_many categories
category has_many products
如果我理解正确,我需要创建一个连接 table products_categories,它有一个 product_id
和一个 category_id
。首先,我还需要这个 table 的模型吗?如果是,我想它看起来像这样:
class CategoryProduct < ActiveRecord::Base
belongs_to :category
belongs_to :product
end
和 product.rb 中的其他模型:
class Product < ActiveRecord::Base
has_many :category_products
has_many :categories, through: :category_product
has_attached_file :picture,
styles: { medium: "300x300>", thumb: "100x100>" }
validates_attachment_content_type :picture,
content_type: /\Aimage\/.*\z/
validates :price, presence: { message: "Merci d'indiquer le prix du produit" }
validates :name, presence: { message: "Merci d'indiquer le nom du produit" }
validates :weight, presence: { message: "Merci d'indiquer le poids du produit" }
validates :description, presence: { message: "Merci d'écrire une description du produit " }
end
并在 category.rb
class Category < ActiveRecord::Base
has_many :category_products
has_many :products, through: :category_product
validates :name, presence: { message: "Merci d'indiquer le nom de la catégorie" }
end
现在假设我想创建一个产品,在创建它的同时,从类别列表中为该产品指定尽可能多的类别。
到目前为止,这是我的 Product/new.html.slim 观点:
div class="container marged-top"
div class= "col-xs-12 col-md-offset-3 col-md-5 bigmarge"
div class="panel panel-default"
div class= "panel-heading"
h4 Création Produit
div class= "panel-body"
=simple_form_for @product, html: { multipart: true } do |t|
= t.error_notification
= t.input :name, label: 'Nom'
= t.input :description, label: 'Description', required: true
= t.input :price, label: 'Prix', required: true
= t.input :weight, label: 'Poids', required: true
= t.label :picture
= t.file_field :picture
= t.association :categories, as: :check_boxes
= t.button :submit, value: "Valider", class: "btn-success marge-bas"
这是我的 Product 实例的一个简单表单。我想我现在需要一个 CategoryProduct 的表格吗? 如果我希望用户在创建产品时能够向产品添加他想要的任意多个类别,我该如何更改它?
这是我的 category_product table 的迁移文件:
class CreateTableCategoriesProducts < ActiveRecord::Migration
def change
create_table :categories_products do |t|
t.references :product, index: true
t.references :category, index: true
end
add_foreign_key :categories_products, :categories
add_foreign_key :categories_products, :products
end
end
我用以下迁移文件重命名了之前的 table :
class RenameTableCategoriesProducts < ActiveRecord::Migration
def self.up
rename_table :categories_products, :category_products
end
def self.down
rename_table :category_products, :categories_products
end
end
我在 product/new.html.slim 中的 simple_form 上收到以下错误:
undefined method `klass' for nil:NilClass
代码在这里中断:
= t.association :categories, as: :check_boxes
所以我想我的联想还是不太正确
您还必须为每个模型添加 CategoryProduct:
class Product < ActiveRecord::Base
has_many :category_products
has_many :categories, through: :category_product
使用gemsimple form
非常简单。您所要做的就是添加:
t.association :categories
在产品表单中,并将 :category_ids => []
添加到产品控制器中允许的参数列表中
如果您更喜欢复选框而不是多select列表,您可以
t.association :categories, as: check_boxes
最后一件事,要以人类可读的格式显示类别,您需要在类别模型中定义一个 to_s
方法,即。即:
class Category < ActiveRecord::Base
...
def to_s
name
end
end
在Rails中有两种方式建立多对多关系:
has_and_belongs_to_many
在没有干预模型的情况下建立多对多关系。
class Category < ActiveRecord::Base
has_and_belongs_to_many :products
end
class Product < ActiveRecord::Base
has_and_belongs_to_many :categories
end
如果您知道您不需要存储任何关于关系的附加数据或添加任何附加功能,这是一个不错的选择——这在实践中实际上很少见。它使用更少的内存,因为它不必为了 product.category
.
使用 has_and_belongs_to_many
时,惯例是连接 table 以两个实体的复数形式命名。
Category + Product = products_categories
顺序似乎并不重要。
has_many 通过:
正如您已经猜到的那样,它使用了中间模型。
class CategoryProduct < ActiveRecord::Base
belongs_to :product
belongs_to :category
end
class Category < ActiveRecord::Base
has_many :category_products
has_many :products, through: :category_products
end
class Product < ActiveRecord::Base
has_many :category_products
has_many :categories, through: :category_products
end
这里的优点是您可以在描述关系的连接 table 中存储和检索附加数据。例如,如果你想存储谁将产品添加到类别 - 或者何时创建关系。
为了Rails能够正确找到ProductCategory class has_many though
命名约定为:
model 1(singular) + model 2(plural)
Product + Category = category_products
这是由于 rails 根据 table 名称推断模型 class 的方式所致。使用 categories_products
将大小写 rails 查找 Category::CategoriesProduct
,因为复数词被解释为模块。然而,这实际上只是一个懒惰的命名约定,通常有一个名词可以更好地描述 A 和 B 之间的关系(例如 Categorization
)。
表单和控制器中的多对多。
正如 IvanSelivanov 已经提到的,SimpleForm 具有用于创建选择、复选框等的辅助方法
但是您可能希望改用 label_method 选项,而不是覆盖模型中的 .to_s
方法。
f.assocation :categories, as: :checkboxes, label_method: :name
覆盖 .to_s
会使调试更加困难,并且在某些情况下会给出令人困惑的测试错误消息。
要将控制器中的参数列入白名单,您可以这样做:
class ProductsController < ApplicationController
def create
@product = Product.new(product_params)
if @product.save
redirect_to @product
else
render :new
end
end
def product_params
params.require(:product)
.permit(:name, :categories_ids, ...)
end
end