My full code can be seen at https://github.com/andyw8/simpleform_examples
I have a join model ProductCategory
with the following validations:
validates :product, presence: true
validates :category, presence: true
My Product
model has the following associations:
has_many :product_categories
has_many :categories, through: :product_categories
When I try to create a new product with a category, the call to @product.save!
in the controller fails with:
Validation failed: Product categories is invalid
When I remove the validations, everything works and the join models are saved correctly.
I'm using strong_parameters
but I don't think that should be related to this issue.
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)
validates :product, presence: true, if: :direct_access?
validates :product, presence: true, on: "update"
... But indeed these are all compromises or workarounds from the simple @product.create(params)
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With