83 votes

Rails - Bonnes pratiques: comment créer des relations has_one dépendantes

Pourriez-vous me dire quelle est la meilleure pratique pour créer des relations has_one?

Fe si j'ai un modèle d'utilisateur, et il doit avoir un profil ...

Comment pourrais-je accomplir cela?

Une solution serait:

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

  def set_default_association
    self.create_profile
  end
end
 

Mais cela ne semble pas très propre ... Tout suggère?

134voto

Larry K Points 16266

Les meilleures pratiques pour créer has_one rapport est d'utiliser ActiveRecord rappel before_create plutôt que d' after_create. Ou utiliser encore plus de rappel et de traiter les problèmes (le cas échéant) de l'enfant de ne pas dépasser ses propres étape de validation.

Parce que:

  • avec un bon codage, vous avez la possibilité pour l'enfant de l'enregistrement des validations à être montré à l'utilisateur si la validation échoue
  • c'est plus propre et explicitement pris en charge par ActiveRecord -- AR remplit automatiquement dans la clé étrangère de l'enfant enregistrement après, il économise de l'enregistrement parent (à créer). AR enregistre ensuite l'enregistrement enfant dans le cadre de la création de l'enregistrement parent.

Comment le faire:

# 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

30voto

bjeanes Points 2835

Votre solution est certainement une façon décente de le faire (au moins jusqu'à ce que vous deveniez trop grand), mais vous pouvez la simplifier:

 # user.rb
class User < ActiveRecord::Base
  has_one      :profile
  after_create :create_profile
end
 

29voto

inopinatus Points 141

S'il s'agit d'une nouvelle association dans une grande base de données existante, je gérerai la transition comme suit:

 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
 

de sorte que les enregistrements d’utilisateurs existants acquièrent un profil à la demande de celui-ci et en créent de nouveaux. Cela place également les attributs par défaut au même endroit et fonctionne correctement avec includes_nested_attributes_for à partir de Rails 4.

9voto

Andrew Vilcsak Points 2111

Ce n’était probablement pas la solution la plus propre, mais nous avions déjà une base de données avec un demi-million d’enregistrements, dont certains avaient déjà le modèle 'Profile' créé, et d’autres non. Nous avons opté pour cette approche, qui garantit qu'un modèle de profil est présent à tout moment, sans avoir à passer en revue et à générer de manière rétroactive tous les modèles de profil.

 alias_method :db_profile, :profile
def profile
  self.profile = Profile.create(:user => self) if self.db_profile.nil?
  self.db_profile
end
 

5voto

Brendon Muir Points 1862

Voici comment je le fais. Pas sûr de la façon standard de ce qui est, mais il fonctionne très bien et son paresseux de ne pas créer une charge supplémentaire, sauf si elle est nécessaire à la construction de la nouvelle association (je suis heureux d'être corrigé sur ce point):

def profile_with_auto_build
  build_profile unless profile_without_auto_build
  profile_without_auto_build
end

alias_method_chain :profile, :auto_build

Cela signifie également que l'association est là dès que vous en avez besoin. Je suppose que l'alternative est de s'accrocher dans after_initialize mais cela semble ajouter un peu de surcharge comme il est exécuté chaque fois qu'un objet est initialisé et il peut y avoir des moments où vous ne vous souciez pas de l'accès à l'association. Il semble comme un gaspillage de vérifier son existence.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X