How do you set a has_one default value in ActiveRecord?
I have something like this:
class User < ActiveRecord::Base
has_one :profile
end
class Profile < ActiveRecord::Base
belongs_to :user
end
user = User.new
user.profile.something #=> ERROR
What is a proper way to set a default profile object in this case? I have tried this:
class User < ActiveRecord::Base
default_scope :include => :profile
has_one :profile
def after_initialize
self.profile ||= Profil开发者_开发问答e.new(:user => self)
end
end
...but that creates N+1 queries. Any ideas?
Update
This is what I have now, works okay, still looking for something better:
class User < ActiveRecord::Base
default_scope :include => :profile
has_one :profile, :autosave => true
def after_initialize
self.profile = Profile.new(:user => self) if new_record?
end
end
This way, you're going to have a Profile whenever you finally create
your user. Otherwise, the only case is a new_record?
.
You can write your own User#profile that will build one for you if it doesn't exist:
class User < ActiveRecord::Base
has_one :profile
def profile_with_default
profile_without_default || build_profile
end
alias_method_chain :profile, :default
end
This is a good answer:
class User < ActiveRecord::Base
has_one :preference_set
def preference_set
super || build_preference_set
end
end
I think your answer is good. I've got a slightly different solution:
class User < ActiveRecord::Base
default_scope :include => :profile
has_one :profile
alias_method :my_profile, :profile
def my_profile
self.profile = Profile.create(:user => self) unless self.profile
self.profile
end
end
Good
- create profile when requested, not on instantiation
Not so good
- You have to use
my_profile
(or however you would like to call it) - The
unless self.profile
check has to be done on each profile call
The correct answer relies on what are your intentions, cause there is no straight forward solution to this kind of problem.
The after_initialize callback is called after object is instantiated, so it's not really a good place for this kind of logic.
Maybe you should try to use before_create / after_create instead? Those callbacks are called only at object creation time.
Also, don't use Profile.new, use one of the methods below instead:
self.build_profile(...)
self.create_profile(...)
In the 2nd case the model is being saved. You can pass a hash with model attributes to both methods (don't pass :user, as it is set automatically).
精彩评论