views:

72

answers:

2

Could you tell me whats the best practice to create has_one relations?

f.e. if i have a user model, and it must have a profile...

How could i accomplish that?

One solution would be:

# user.rb
class User << ActiveRecord::Base
  after_create :set_default_association

  def set_default_association
    self.create_profile
  end
end

But that doesnt seem very clean... Any suggests?

+3  A: 

Your solution is definitely a decent way to do it (at least until you outgrow it), but you can simplify it:

# user.rb
class User << ActiveRecord::Base
  has_one      :profile
  after_create :create_profile
end
bjeanes
Thx, cool hint!
Lichtamberg
+3  A: 

Best practice to create has_one relation is to use the ActiveRecord callback before_create rather than after_create. Or use an even earlier callback and deal with the issues (if any) of the child not passing its own validation step.

Because:

  • with good coding, you have the opportunity for the child record's validations to be shown to the user if the validations fail
  • it's cleaner and explicitly supported by ActiveRecord -- AR automagically fills in the foreign key in the child record after it saves the parent record (on create). AR then saves the child record as part of creating the parent record.

How to do it:

# in your User model...
has_one :profile
before_create :build_default_profile

private
def build_default_profile
  # build default profile instance. Will use default params.
  # The foreign key to the owning User model is set automatically
  build_profile
  true # Always return true in callbacks as the normal 'continue' state
       # Assumes that the default_profile can **always** be created.
       # or
       # Check the validation of the profile. If it is not valid, then
       # return false from the callback. Best to use a before_validation 
       # if doing this. View code should check the errors of the child.
       # Or add the child's errors to the User model's error array of the :base
       # error item
end
Larry K
+1 for awareness of child validation.
PeterWong
Could that be also managed with a single line? -> before_filter :build_profile ?
Lichtamberg
@Lichtamberg: Yes, but I'd add a comment: "Builds default profile. MUST always validate." NOTE: it'd be "before_create :build_profile" not 'before_filter'. If it didn't validate then you'd get a very confusing error msg to the user. Or it would NOT in fact be created which would mean you'd end up with a User without a profile. You should also test the corner cases in your tests.
Larry K
Thanks for pointing out this usefull information..
Lichtamberg