Pourriez-vous me dire quelle est la meilleure pratique pour créer des relations has_one?
f.e. si j'ai un modèle utilisateur et qu'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?
La meilleure pratique pour créer une relation has_one consiste à utiliser le rappel ActiveRecord before_create
plutôt que after_create
. Ou utilisez un rappel encore plus tôt et résolvez les problèmes (le cas échéant) de l'enfant qui ne passe pas sa propre étape de validation.
Car:
Comment 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
Votre solution est certainement une façon décente de le faire (au moins jusqu'à ce que vous la dépassiez), mais vous pouvez la simplifier:
# user.rb
class User < ActiveRecord::Base
has_one :profile
after_create :create_profile
end
S'il s'agit d'une nouvelle association dans une grande base de données existante, je gérerai la transition comme ceci:
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
afin que les enregistrements d'utilisateurs existants obtiennent un profil lorsqu'on le lui demande et que de nouveaux soient créés avec. Cela place également les attributs par défaut à un seul endroit et fonctionne correctement avec accept_nested_attributes_for dans Rails 4 et suivants).
Ce n'est 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à créé le modèle "Profil", et d'autres non. Nous avons opté pour cette approche, qui garantit qu'un modèle Profile est présent à tout moment, sans avoir à parcourir et générer rétroactivement tous les modèles Profile.
alias_method :db_profile, :profile
def profile
self.profile = Profile.create(:user => self) if self.db_profile.nil?
self.db_profile
end
Voici comment je le fais. Je ne sais pas à quel point c'est standard, mais cela fonctionne très bien et c'est paresseux en ce qu'il ne crée pas de surcharge supplémentaire à moins qu'il ne soit nécessaire de créer la nouvelle association (je suis heureux d'être corrigé à ce sujet):
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 se connecter à after_initialize mais cela semble ajouter un peu de surcharge car il est exécuté chaque fois qu'un objet est initialisé et il peut y avoir des moments où vous ne vous souciez pas d'accéder à l'association. Il semble être un gaspillage de vérifier son existence.
Il y a un petit bijou pour ça:
https://github.com/jqr/has_one_autocreate
On dirait que c'est un peu vieux maintenant. (ne fonctionne pas avec Rails3)