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

后端 未结 7 617
星月不相逢
星月不相逢 2020-12-04 05:39

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

相关标签:
7条回答
  • 2020-12-04 06:15

    I had an issue with this and accepts_nested_attributes_for because if nested attributes were passed in, the associated model was created there. I ended up doing

    after_create :ensure_profile_exists
    has_one :profile
    accepts_nested_attributes_for :profile
    
    
    def ensure_profile_exists
      profile || create_profile
    end
    
    0 讨论(0)
  • 2020-12-04 06:24

    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")
      end
    
    private
      def build_associations
        profile || true
      end
    end
    

    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.

    0 讨论(0)
  • 2020-12-04 06:26

    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
    
    0 讨论(0)
  • 2020-12-04 06:30

    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
    
    0 讨论(0)
  • 2020-12-04 06:31

    There is a gem for this:

    https://github.com/jqr/has_one_autocreate

    Looks like it is a bit old now. (not work with rails3)

    0 讨论(0)
  • 2020-12-04 06:35

    Probably not the cleanest solution, but we already had a database with half a million records, some of which already had the 'Profile' model created, and some of which didn't. We went with this approach, which guarantees a Profile model is present at any point, without needing to go through and retroactively generate all the Profile models.

    alias_method :db_profile, :profile
    def profile
      self.profile = Profile.create(:user => self) if self.db_profile.nil?
      self.db_profile
    end
    
    0 讨论(0)
提交回复
热议问题