Attach table for has_many via Rails

I am new to programming and rails and I don’t understand something. I am creating an application with

product has_many categories category has_many products 

If I understand correctly, I need to create a products_categories connection table that has product_id and category_id . To start, I also need a model for this table? if so, I think it will look like this:

 class CategoryProduct < ActiveRecord::Base belongs_to :category belongs_to :product end 

and other models in 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 

and in 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 

Now let's say that I want to create a product, and while I am creating it, specify as many categories as I want for this product from the list of categories.

So far, this has been my Product / new.html.slim in my views:

  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" 

This was a simple form for my product instance. I guess I need to have a form for CategoryProduct now? How can I change this if I want the user to be able to add as many categories that he wants to the product while he creates it?

Here is my migration file for the 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 

I renamed the previous table with the following migration file:

 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 

I get the following error in simple_form in product / new.html.slim:

 undefined method `klass' for nil:NilClass 

Here the code breaks:

  = t.association :categories, as: :check_boxes 

so i think my associations arent still quite right

+5
source share
2 answers

There are two ways to implement many-to-many relationships in Rails:

has_and_belongs_to_many

establishes a multitude to many relationships without an intermediate model.

 class Category < ActiveRecord::Base has_and_belongs_to_many :products end class Product < ActiveRecord::Base has_and_belongs_to_many :categories end 

It’s good if you know that this is a simple direct relationship and you know that you do not need to store any additional relationship data. It uses less memory since it is not necessary to instantiate an additional model for product.category only.

When using has_and_belongs_to_many convention is that the join table is named after two plural objects. In alphabet order:

 Category + Product = products_categories 

has_many through

you guessed it, uses an intermediate model.

 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 

The advantage is that you can store and retrieve additional data in the connection table, which describes the relationship. For example, if you want to save who added the product to the category - or when the link was created.

So that Rails can correctly find the ProductCategory class, the connection table for the has_many though naming convention has_many though

 model 1(singular) + model 2(plural) Product + Category = category_products 

This is because the rails define the model class based on the table name. Using categories_products will cause the rails to search for Category::CategoriesProduct .

From many to many in forms and controllers.

As Ivan Selivanov has already said, SimpleForm has helper methods for creating selections, checkboxes, etc.

Instead of overriding the .to_s method in your model, you can use the label_method parameter instead.

 f.assocation :categories, as: :checkboxes, label_method: :name 

Overriding .to_s can complicate debugging and in some cases lead to confusing error messages.

For a white list of parameters in your controller you will do:

 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 
+4
source

You also need to add CategoryProduct to each model:

 class Product < ActiveRecord::Base has_many :category_products has_many :categories, through: :category_product 

Very simple to use gem simple form . All you have to do is add:

 t.association :categories 

in the product form and add :category_ids => [] to the list of allowed parameters in your product controller

If you prefer checkboxes over multiple choice lists, you can do

  t.association :categories, as: check_boxes 

Lastly, to display categories in a readable format, you need to define the to_s method in your category model, i.e. e :.

 class Category < ActiveRecord::Base ... def to_s name end end 
+1
source

Source: https://habr.com/ru/post/1233374/


All Articles