Rails - Best-Practice: How to create dependent has_one relations Rails - Best-Practice: How to create dependent has_one relations ruby ruby

Rails - Best-Practice: How to create dependent has_one relations


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 :profilebefore_create :build_default_profileprivatedef 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 itemend


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

# user.rbclass User < ActiveRecord::Base  has_one      :profile  after_create :create_profileend


If this is a new association in an existing large database, I'll manage the transition like this:

class User < ActiveRecord::Base  has_one :profile  before_create :build_associations  def profile    super || build_profile(avatar: "anon.jpg")  endprivate  def build_associations    profile || true  endend

so that existing user records gain a profile when asked for it and new ones are created with it. This also places the default attributes in one place and works correctly with accepts_nested_attributes_for in Rails 4 onwards.