Say I have a User
model, a Task
model that belongs_to :user
, :has_one :event
, has a completed
boolean attribute, and an Event
model that is created when a task is completed, and also belongs_to :event
.
In the TaskObserver
I've noticed that instead of
# app/controllers/task_observer.rb
class TaskObserver < ActiveRecord::Observer
def after_update(task)
def after_update
task.create_event(:us开发者_开发技巧er=>task.user) if task.completed?
end
end
I could write
task.create_event(:user_id=>task.user.id)
or even
task.create_event(:user_id=>task.user_id)
While the first way seems the most correct, are there benefits to using either of the latter variations?
In Rails, associations can be assigned either way, neither is "right" or "wrong" and it's just the nature of the framework. The models has setter methods for both user_id
and user
, which is why you can use either without any noticeable difference.
The way you are creating events seems a little strange to me though. It seems really odd that a task would belong_to an event, but the event is only created when the task is complete. Is that really how it works?
As Beerlington said - there is no "right" or "wrong" here - perhaps a performance consideration to be had however...
In the task.user.id case, if the user isn't eager-loaded, you're making a round trip to the database; in the task.user_id case, you're not making that round trip....
- Note - Rails is probably smart enough to notice that if you're just grabbing id off the association it can just use task.user_id - but I haven't gone to great lengths to confirm this. Would be easy enough to check against the development.log....
In this specific case I went with task.create_event(:user_id=>task.user_id)
. By running:
$ rails c
ruby-1.8.7-p299 > ActiveRecord::Base.logger = Logger.new(STDOUT)
ruby-1.8.7-p299 > Task.where("user_id IS NOT NULL).user.id
...
User Load (1.2ms) SELECT `users`.* FROM `users` WHERE (`users`.`id` = 103) LIMIT 1
=> 103
you can see that Rails will actually load the User
from the database. Even if it was cached, I don't see why I should handle the actual objects if I'm just copying a reference.
So in general, I think it's preferred to use objects when you've used them before, and IDs when you didn't and don't plan to.
精彩评论