J'ai les 2 modèles suivants
class Sport < ActiveRecord::Base
has_many :charts, order: "sortWeight ASC"
has_one :product, :as => :productable
accepts_nested_attributes_for :product, :allow_destroy => true
end
class Product < ActiveRecord::Base
belongs_to :category
belongs_to :productable, :polymorphic => true
end
Un sport ne peut pas exister sans le produit, donc dans mon sports_controller.rb
J'ai eu:
def new
@sport = Sport.new
@sport.product = Product.new
...
end
J'ai essayé de déplacer la création du produit vers le modèle sport en utilisant after_initialize
:
after_initialize :create_product
def create_product
self.product = Product.new
end
J'ai rapidement appris que after_initialize
est appelé chaque fois qu'un modèle est instancié (c'est-à-dire à partir d'un appel find
). Ce n'était donc pas le comportement que je recherchais.
Quelle est la façon dont je devrais modéliser l'exigence que tous les sport
aient un product
?
Merci
Mettre la logique dans le contrôleur pourrait être la meilleure réponse comme vous l'avez dit, mais vous pouvez obtenir le after_initialize
pour travailler en procédant comme suit:
after_initialize :add_product
def add_product
self.product ||= Product.new
end
De cette façon, il ne définit le produit que si aucun produit n'existe. Cela peut ne pas valoir la surcharge et/ou être moins clair que d'avoir la logique dans le contrôleur.
Edit: Selon la réponse de Ryan, en termes de performances, ce qui serait probablement mieux:
after_initialize :add_product
def add_product
self.product ||= Product.new if self.new_record?
end
Sûrement after_initialize :add_product, if: :new_record?
est le moyen le plus propre ici.
Gardez le conditionnel hors de la fonction add_product
Si tu fais self.product ||= Product.new
il cherchera toujours un produit chaque fois que vous effectuez un find
car il doit vérifier s'il est nul ou non. En conséquence, il n'effectuera aucun chargement ardent. Pour ce faire uniquement lorsqu'un nouvel enregistrement est créé, vous pouvez simplement vérifier s'il s'agit d'un nouvel enregistrement avant de configurer le produit.
after_initialize :add_product
def add_product
self.product ||= Product.new if self.new_record?
end
J'ai fait quelques benchmarks et vérifications de base if self.new_record?
ne semble pas affecter les performances de manière notable.
À la place d'utiliser after_initialize
, que diriez-vous after_create
?
after_create :create_product
def create_product
self.product = Product.new
save
end
Est-ce que cela résoudrait votre problème?
On dirait que tu es très proche. Vous devriez pouvoir supprimer complètement l'appel after_initialize, mais je pense d'abord que si votre modèle Sport a une relation "has_one" avec: produit comme vous l'avez indiqué, votre modèle de produit devrait également "appartenir" au sport. Ajoutez ceci à votre modèle de produit
belongs_to: :sport
L'étape suivante, vous devriez maintenant pouvoir instancier un modèle Sport comme ça
@sport = @product.sport.create( ... )
Ceci est basé sur les informations de Association Basics from Ruby on Rails Guides, que vous pourriez lire si je le suis) pas exactement correct
Vous devez simplement remplacer la méthode d'initialisation comme
class Sport < ActiveRecord::Base
# ...
def initialize(attributes = {})
super
self.build_product
self.attributes = attributes
end
# ...
end
La méthode d'initialisation n'est jamais appelée lorsque l'enregistrement est chargé à partir de la base de données. Notez que dans le code ci-dessus, les attributs sont attribués après la création du produit. Dans un tel paramètre, l'attribution des attributs peut affecter l'instance de produit créée.