Failing validations in join model when using has_many :through

后端 未结 3 1248
被撕碎了的回忆
被撕碎了的回忆 2021-02-03 10:23

My full code can be seen at https://github.com/andyw8/simpleform_examples

I have a join model ProductCategory with the following validations:



        
相关标签:
3条回答
  • 2021-02-03 10:46

    Specifying inverse_of on your joining models has been documented to fix this issue:

    https://github.com/rails/rails/issues/6161#issuecomment-6330795 https://github.com/rails/rails/pull/7661#issuecomment-8614206

    Simplified Example:

    class Product < ActiveRecord::Base
      has_many :product_categories, :inverse_of => :product
      has_many :categories, through: :product_categories
    end
    
    class Category < ActiveRecord::Base
      has_many :product_categories, inverse_of: :category
      has_many :products, through: :product_categories
    end
    
    class ProductCategory < ActiveRecord::Base
      belongs_to :product
      belongs_to :category
    
      validates :product, presence: true
      validates :category, presence: true
    end
    
    Product.new(:categories => [Category.new]).valid? # complains that the ProductCategory is invalid without inverse_of specified
    

    Adapted from: https://github.com/rails/rails/issues/8269#issuecomment-12032536

    0 讨论(0)
  • 2021-02-03 11:07

    Pretty sure you just need to define your relationships better. I still might have missed some, but hopefully you get the idea.

    class Product < ActiveRecord::Base
      include ActiveModel::ForbiddenAttributesProtection
    
      validates :name, presence: true
      validates :description, presence: true
      validates :color_scheme, presence: true
    
      belongs_to :color_scheme
    
      has_many :product_categories, inverse_of: :product
      has_many :categories, through: :product_categories
    end
    
    
    class ProductCategory < ActiveRecord::Base
      belongs_to :product
      belongs_to :category
    
      validates_associated :product
      validates_associated :category
    
      # TODO work out why this causes ProductsController#create to fail
      # validates :product, presence: true
      # validates :category, presence: true
    end
    
    
    class Category < ActiveRecord::Base
      has_many :product_categories, inverse_of: :category
      has_many :products, through: :product_categories
    end
    
    0 讨论(0)
  • This is a "racing condition" in the callback chain.

    When you create a product it doesn't have any id before it is saved, therefore there is no product in the scope of ProductCategory.

    Product.new(name: "modern times", category_ids:[1, 2]) #=> #<Product id: nil >
    

    At that stage of validation (before saving), ProductCatgory cannot assign any id to it's foreign key product_id.

    That's the reason you have association validations : so that the validation happens in the scope of the whole transaction

    UPDATE: As said in the comment you still can't ensure presence of a product/category. There's many ways around depending on why you want do this (e.g direct access to ProductCategory through some form)

    • You can create a flag to have validates :product, presence: true, if: :direct_access?
    • or if you can only update them: validates :product, presence: true, on: "update"
    • create your product first (in the products_controller) and add the categories after

    ... But indeed these are all compromises or workarounds from the simple @product.create(params)

    0 讨论(0)
提交回复
热议问题