使用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