开发者

How to bulk validate association in Rails

I have the following scenario:

One of my models, let's call it 'Post', has multiple associated models, Images.

One, and only one, of those images can be the key Image to its Post (that is represented as a boolean flag on the Image model and enforced through a validation on the Image model which uses the Post as its scope).

Now of course when I want to update the primary Image flag, it happens that an Image model's key flag is set to true and the validation fails because there's still another Image with the key flag set to true.

I know, that thin开发者_如何学Cg screams to be transformed into an association on the Post model, which links to the key Image, but is there a way to validate associations in bulk in Rails?

What would be your take, would you make the key Image a separate association on the Post model or would you use the boolean flag?


there is a simple solution but it needs some trust:

  • Remove the validation "is there only one primary image?"
  • Make sure there will be only one primary image by adding a filter

The big plus is that you don't have to check anything in your controller or post model. Just take an image, set is_primary to true and save it.

So the setup could look like:

class Post < ActiveRecord::Base
  has_many  :images 

  # some sugar, @mypost.primary_image gets the primary image
  has_one   :primary_image, 
            :class_name => "Image", 
            :conditions => {:is_primary => true }
end

class Image < ActiveRecord::Base
  belongs_to :post

  # Image.primary scopes on primary images only
  scope :primary, where(:is_primary => true)

  # we need to clear the old primary if:
  # this is a new record and should be primary image
  # this is an existing record and is_primary has been changed to true

  before_save :clear_primary, 
              :if => Proc.new{|r| (r.new_record? && r.is_primary) || (r.is_primary_changed? && r.is_primary) }

  def clear_primary
    # remove old primary image
    Image.update_all({:is_primary => false}, :post_id => self.post_id)
  end
end

Edit:

This will work in any case - why?

  • before_save is only invoked if all validations succeed
  • the whole save is wrapped in a transaction, this means if clear_primary or the saving of the image itself fails, everyhing will be rolled back to it's original state.


Well you can do this within your Post model:

# Post.rb
has_many :images, :conditions => ['primary = ?', false]
has_one :primary_image, :conditions => ['primary = ?', true]

When you want to change the primary image, do something like this:

# Post.rb
def new_primary_image(image_id)
    primary_image.primary = false
    Image.find(image_id).primary = true
end
0

上一篇:

下一篇:

精彩评论

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

最新问答

问答排行榜