开发者

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])
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜