使用has_many:through时,连接模型中的验证失败

时间:2013-01-03 14:38:53

标签: ruby-on-rails activerecord

我的完整代码可以在https://github.com/andyw8/simpleform_examples

看到

我有一个联接模型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

删除验证后,一切正常,连接模型也会正确保存。

我正在使用strong_parameters,但我认为应与此问题相关。

3 个答案:

答案 0 :(得分:11)

这是回调链中的“竞争条件”。

创建产品时,在保存之前它没有任何ID,因此product范围内没有ProductCategory

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

在验证阶段(保存之前),ProductCatgory无法为其外键product_id分配任何ID。

这就是你有关联验证的原因:这样验证就发生在整个交易的范围内

更新:正如评论中所说,您仍然无法确保产品/类别的存在。有很多方法取决于你想要这样做的原因(例如通过某种形式直接访问ProductCategory)

  • 您可以创建一个包含validates :product, presence: true, if: :direct_access?
  • 的标记
  • 或者如果您只能更新它们:validates :product, presence: true, on: "update"
  • 首先创建您的产品(在products_controller中)并在
  • 之后添加类别

......但事实上,这些都是简单@product.create(params)

的妥协或解决方法

答案 1 :(得分:3)

已记录在您的加入模型上指定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

答案 2 :(得分:0)

非常确定您需要更好地定义您的关系。我仍然可能错过了一些,但希望你能得到这个想法。

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