开发者

How do you set a has_one default value in ActiveRecord?

开发者 https://www.devze.com 2023-01-16 21:46 出处:网络
I have something like this: class User < ActiveRecord::Base has_one :profile end class Profile < ActiveRecord::Base

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).

0

精彩评论

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

关注公众号