开发者

Rails: hook for validating model when it's appended to its parent resource?

I have a situation where a user submits a multi-model form. Through that form, I have to assign the user a random item from my database along to their account. As I loop through the data, I flag the item that I've assigned to them with a boolean in_use flag. The problem is, in a situation such as the following:

2.times do |n|
   # grab random item which will be parent
   parent = # some random code to grab an item not in use
   parent.i开发者_StackOverflow中文版n_use = 1
   parent.child.build
   children << child
end

Three models involved here. The parent itself is within its own parent, hence the children << child statement. The problem here is an edge condition is that the code that grabs a random item not in use can grab the same parent twice as I don't know of a hook that will allow me to save parent.in_use after the child has been appended to its parent via children << child. The loop will go again, the in_use flag hasn't been persisted to the database and it can select it again. Is there a way to persist it, then roll it back if validation fails in a situation like this?


Does this work?

2.times do |n|
  # grab random item which will be parent
  parent = # some random code to grab an item not in use
  parent.child.build
  parent.in_use
  parent.save # <--- save the parent on the db
  children << child
end

I'm not sure about the last line, since you didn't explain what the children variable was. I'm also assuming that parent.in_use is a method, not a property (otherwise you would have to write parent.in_use = true or something similar)

One more thing - it seems you are using an external attribute (called in_use or similar) in order to store whether a parent has children. It would be probably simpler just to count the number of children. There are several ways to do this, but a good compromise is using an automatic counter cache.

class Parent << ActiveRecord::Base
  has_many :children, :counter_cache => true #this will had a children_count attribute

The attribute is named just like the children table. So if you write has_many :issues, the counter will be issues_Count.

You will have to add a children_count attribute to the parent's table.

You then can do if parent.children_count > 0 instead of checking strange parent.in_use attributes.

More information on the counter cache on railscast#23


Your question is frustrating, because you say your issue stems from pulling in the same parent twice, but the code to pull the parent is omitted. Yet the code for an irrelevant confusing contextless 3 model relationship is left in.

Anyway, I'm going to say this how it seems from my perspective, and I think you'll agree: The solution you are asking for has you getting the same parent twice (now in two places in memory with different state between them) building relationships that shouldn't exist, somehow figuring this out in your validations, having a conditional in the loop to check for it and retry the whole process.

From my perspective, this sounds like a nightmare. As someone who has written horribly convoluted bug inclined code like that, I strongly recommend you just get two different parents in the first place. Don't let errors propagate across your app, contain them early, or better yet, prevent them from happening at all:

class YourUnnamedModel < ActiveRecord::Base
  named_scope :unused  , :conditions => { :in_use => false }
  named_scope :random  , :order => 'RANDOM()'
  named_scope :limited , lambda { |n=1| return :limit => n }

  def self.example
    unused.random.limited(2).each do |parent|
      puts "doing stuff with #{parent.inspect}"
    end
    nil
  end

end

Caveat: For some stupid reason (https://rails.lighthouseapp.com/projects/8994/tickets/1274-patch-add-support-for-order-random-in-queries), it is possible that your 'RANDOM()' could be called something else.

0

上一篇:

下一篇:

精彩评论

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

最新问答

问答排行榜