Sooner or later, the need arises to assign default values to model attributes. In rails this is usually done in model’s after_initialize() with a new_record? guard.
class User def after_initialize if new_record? self.country = "US" end end end
This initializes the object with proper default so the UI form gets properly populated and the database gets proper default if user hasn’t overridden it.
Sometimes, you realize that you have (pre-existing?) objects in database that also need the same default if the current value is nil. In such case, you may modify your initializer like this:
class User def after_initialize self.country ||= "US" end end
This works for both scenarios: User.new and User.find.
However, it introduces a behavior that may not be apparant immediately.
$ script/console ree> User.find(u.id) User Load (0.9ms) SELECT * FROM `users` WHERE (`users`.`id` = 1) ree> User.exists?(:id => u.id) RateSearch Load (0.6ms) SELECT `users`.id FROM `users` WHERE (`users`.`id` = 1) LIMIT 1 ActiveRecord::MissingAttributeError: missing attribute: country /[prj]/app/models/User.rb:3:in `after_initialize'
A closer look reveals that ActiveRecord tries to be smart and only fetch ‘id’ column when performing Model.exists? call.
Many people have tripped this and logged it as a bug. The report has been silently ignored, and I believe, for good/performance reasons.
So, what do we get around it? Here’s one way.
class User def after_initialize self.country ||= "US" rescue ActiveRecord::MissingAttributeError # this should only happen on Model.exists?() call. It can be safely ignored. end end
As they say, it is not a bug, it is a feature.
No related posts.
Related posts brought to you by Yet Another Related Posts Plugin.