在Rails中加入has_many的表

Dav*_*mar 7 forms model ruby-on-rails jointable

我是编程和轨道的新手,有一些我不完全理解的东西.我正在创建一个应用程序

product has_many categories
category has_many products
Run Code Online (Sandbox Code Playgroud)

如果我理解正确,我需要创建一个具有product_id&a 的连接表products_categories category_id.首先,我还需要这个表的模型吗?如果是的话,我想它会是这样的:

class CategoryProduct < ActiveRecord::Base
   belongs_to :category
   belongs_to :product
end
Run Code Online (Sandbox Code Playgroud)

和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
Run Code Online (Sandbox Code Playgroud)

并在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
Run Code Online (Sandbox Code Playgroud)

现在假设我要创建一个产品,在创建产品时,请从类别列表中为此产品指定尽可能多的类别.

到目前为止,这是我的视图中的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"
Run Code Online (Sandbox Code Playgroud)

这是我的Product实例的简单表单.我想我现在需要为CategoryProduct提供表格吗?如果我希望用户能够在创建产品时添加他想要的产品类别,我该如何更改?

这是category_product表的迁移文件:

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
Run Code Online (Sandbox Code Playgroud)

我使用以下迁移文件重命名了上一个表:

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
Run Code Online (Sandbox Code Playgroud)

我在product/new.html.slim中的simple_form上收到以下错误:

undefined method `klass' for nil:NilClass
Run Code Online (Sandbox Code Playgroud)

代码在这里打破:

 = t.association :categories, as: :check_boxes
Run Code Online (Sandbox Code Playgroud)

所以我猜我的关联还不是很正确

max*_*max 10

在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
Run Code Online (Sandbox Code Playgroud)

如果您知道这是一个简单的直接关系并且您知道您不需要存储关于该关系的任何其他数据,那么这很好.它使用较少的内存,因为它不需要实例化额外的模型product.category.

使用has_and_belongs_to_many约定时,连接表以复数形式的两个实体命名.在alfabetical顺序:

Category + Product = products_categories
Run Code Online (Sandbox Code Playgroud)

has_many through

正如您已经猜到的那样使用中间模型.

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
Run Code Online (Sandbox Code Playgroud)

这里的优点是您可以在连接表中存储和检索描述关系的其他数据.例如,如果您想存储将产品添加到类别的人员 - 或者创建关系时.

为了使Rails能够正确地找到ProductCategory类,has_many though命名约定的连接表是

model 1(singular) + model 2(plural) 
Product + Category = category_products
Run Code Online (Sandbox Code Playgroud)

这是由于rails根据表名推断模型类的方式.使用categories_productscase case rails来寻找Category::CategoriesProduct.

表格和控制器中的多对多.

正如IvanSelivanov已经提到的,SimpleForm有辅助方法来创建选择,复选框等.

但是,.to_s您可能希望使用label_method选项而不是覆盖模型中的方法.

f.assocation :categories, as: :checkboxes, label_method: :name
Run Code Online (Sandbox Code Playgroud)

覆盖.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
Run Code Online (Sandbox Code Playgroud)