使用has_many:through时,连接模型中的validation失败
我的完整代码可以在https://github.com/andyw8/simpleform_examples上看到
我有一个带有以下validation的连接模型ProductCategory
:
validates :product, presence: true validates :category, presence: true
我的Product
型号具有以下关联:
has_many :product_categories has_many :categories, through: :product_categories
当我尝试使用类别创建新产品时,调用@product.save!
在控制器中失败:
Validation failed: Product categories is invalid
当我删除validation时,一切正常,连接模型正确保存。
我正在使用strong_parameters
但我认为不应该与此问题相关。
这是回调链中的“竞争条件”。
创建产品时,在保存product
之前它没有任何ID,因此ProductCategory
范围内没有product
。
Product.new(name: "modern times", category_ids:[1, 2]) #=> #
在validation阶段(保存之前), ProductCatgory
无法为其外键product_id
分配任何ID。
这就是你进行关联validation的原因:这样validation就发生在整个交易的范围内
更新:如评论中所述,您仍然无法确保产品/类别的存在。 有很多方法取决于你想要这样做的原因(例如通过某种forms直接访问ProductCategory)
- 您可以创建一个包含
validates :product, presence: true, if: :direct_access?
的标志validates :product, presence: true, if: :direct_access?
- 或者如果您只能更新它们:
validates :product, presence: true, on: "update"
- 首先创建您的产品(在products_controller中)并在之后添加类别
……但实际上这些都是简单的@product.create(params)
妥协或解决方法
已记录在您的加入模型上指定inverse_of
以解决此问题:
https://github.com/rails/rails/issues/6161#issuecomment-6330795 https://github.com/rails/rails/pull/7661#issuecomment-8614206
简化示例:
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
改编自: https : //github.com/rails/rails/issues/8269#issuecomment-12032536
很确定你只需要更好地定义你的关系。 我仍然可能错过了一些,但希望你能得到这个想法。
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