Join table for has_many through in Rails

后端 未结 2 1663
庸人自扰
庸人自扰 2021-02-13 22:04

I am new to programming & rails and there\'s something I dont fully understand. I am creating an app with

product has_many categories
category has_many produ         


        
2条回答
  •  抹茶落季
    2021-02-13 22:18

    In Rails there are two ways to do many-to-many relationships:

    has_and_belongs_to_many

    sets up a many to many relationship without an intervening model.

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

    This is a good choice if you know that you will not need to store any additional data about the relationship or add any additional functionality - which in practice is actually really rare. It uses less memory since it does not have to instantiate an extra model just to do product.category.

    When using has_and_belongs_to_many the convention is that the join table is named after the two entities in plural. In alfabetical order:

    Category + Product = products_categories
    

    has_many through

    as you already have guessed 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 here is that you can store and retrieve additional data in the join table which describes the relationship. For example if you wanted to store who added a product to a category - or when the relationship was created.

    In order to for Rails to be able to correctly find the ProductCategory class the join table for a has_many though naming convention is

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

    This is due to the way that rails infers the model class based on the table name. Using categories_products would case rails to look for Category::CategoriesProduct.

    Many to Many in forms and controllers.

    As IvanSelivanov already mentioned SimpleForm has helper methods for creating selects, checkboxes etc.

    • https://github.com/plataformatec/simple_form#associations

    But instead of overriding the .to_s method in your model you may want to use the label_method option instead.

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

    Overriding .to_s can make debugging harder and in some cases give confusing test error messages.

    To whitelist the params in your controller you would 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
    

提交回复
热议问题