Rails 3: How to enforce model's validation to fail when the validation of the associated model fails?
I have the following two models:
class Product < ActiveRecord::Base
belongs_to :shop
validates_numericality_of :price, :greater_than_or_equal_to => 0
end
class Shop < ActiveRecord::Base
has_many :products
validates_presence_of :name
end
Here is the create
method of my ProductsController
:
def create
if params[:product][:shop_id] == "new_shop"
@shop = Shop.find_by_name(params[:new_shop]) || Shop.create(:name => params[:new_shop]) # Is there a simpler method to do this ?
params[:product][:shop_id] = @shop.id
end
@product = Product.new(params[:product])
if @product.save
redirect_to(:action => 'index')
else
render('new')
end
end
When user adds a new product he has a select box to choose the shop. The last option in this select box lets user to add a new shop (an additional input text field appears). The value
of this last option is new_shop
.
If the validation of the new 开发者_开发百科entered shop fails, I would like the validation of the product to fail and display an appropriate error (currently an error displayed only if the validation of the product itself fails).
What would be the most "Rails 3 method" to achieve this ?
I think it would be simpler if you use accepts_nested_attributes_for
. So to your Product
model add:
accepts_nested_attributes_for :shop
And then in view depending on your select list value you can modify form (in js), so there will be either shop_id
field or a whole set of fileds for a shop:
<% f.fields_for :shop do |sf| %>
...
<% end %>
Then if user selects existing shop, it will only pass shop_id
, but if users selects new shop, then form will pass also new associated object.
If you want shop name to be unique, then just add validates_uniqueness_of
to Shop
model.
If validation of a shop fails, then product won't be saved. Basicaly, your controller stays as simple as it could be (just creating new product object from params - you don't care about shop there).
I agree with @klew, you should probably be using accepts_nested_attributes_for
.
But, the simple and direct answer to your question is to use validates_associated
.
Also, the nicer way of doing:
@shop = Shop.find_by_name(params[:new_shop]) || Shop.create(:name => params[:new_shop])
would be:
@shop = Shop.find_or_create_by_name(params[:new_shop])
精彩评论