Je souhaite établir une relation polymorphe avec accepts_nested_attributes_for
. Voici le code:
class Contact <ActiveRecord::Base
has_many :jobs, :as=>:client
end
class Job <ActiveRecord::Base
belongs_to :client, :polymorphic=>:true
accepts_nested_attributes_for :client
end
Lorsque j'essaie d'accéder à Job.create(..., :client_attributes=>{...}
Donne moi NameError: uninitialized constant Job::Client
Je viens de comprendre que Rails ne prend pas en charge ce type de comportement, j'ai donc trouvé la solution de contournement suivante:
class Job <ActiveRecord::Base
belongs_to :client, :polymorphic=>:true, :autosave=>true
accepts_nested_attributes_for :client
def attributes=(attributes = {})
self.client_type = attributes[:client_type]
super
end
def client_attributes=(attributes)
self.client = type.constantize.find_or_initialize_by_id(attributes.delete(:client_id)) if client_type.valid?
end
end
Cela me permet de configurer mon formulaire comme ceci:
<%= f.select :client_type %>
<%= f.fields_for :client do |client|%>
<%= client.text_field :name %>
<% end %>
Pas la solution exacte mais l'idée est importante.
J'ai également rencontré un problème avec "ArgumentError: Impossible de créer l'association nom_modèle. Essayez-vous de créer une association un à un polymorphe?"
Et j'ai trouvé une meilleure solution pour ce genre de problème. Vous pouvez utiliser la méthode native. Regardons l'implémentation nested_attributes, à l'intérieur de Rails3:
elsif !reject_new_record?(association_name, attributes)
method = "build_#{association_name}"
if respond_to?(method)
send(method, attributes.except(*UNASSIGNABLE_KEYS))
else
raise ArgumentError, "Cannot build association #{association_name}. Are you trying to build a polymorphic one-to-one association?"
end
end
Alors, en fait, que devons-nous faire ici? Il s'agit simplement de créer la build _ # {association_name} à l'intérieur de notre modèle. J'ai fait un exemple totalement fonctionnel en bas:
class Job <ActiveRecord::Base
CLIENT_TYPES = %w(Contact)
attr_accessible :client_type, :client_attributes
belongs_to :client, :polymorphic => :true
accepts_nested_attributes_for :client
protected
def build_client(params, assignment_options)
raise "Unknown client_type: #{client_type}" unless CLIENT_TYPES.include?(client_type)
self.client = client_type.constantize.new(params)
end
end
J'ai enfin obtenu que cela fonctionne avec Rails 4.x. Ceci est basé sur la réponse de Dmitry/ScotterC, donc +1 pour eux.
ÉTAPE 1. Pour commencer, voici le modèle complet avec association polymorphe:
# app/models/polymorph.rb
class Polymorph < ActiveRecord::Base
belongs_to :associable, polymorphic: true
accepts_nested_attributes_for :associable
def build_associable(params)
self.associable = associable_type.constantize.new(params)
end
end
# For the sake of example:
# app/models/chicken.rb
class Chicken < ActiveRecord::Base
has_many: :polymorphs, as: :associable
end
Oui, ce n'est pas vraiment nouveau. Cependant, vous vous demandez peut-être d'où vient polymorph_type
Et comment sa valeur est-elle définie? Cela fait partie de l'enregistrement de base de données sous-jacent, car les associations polymorphes ajoutent des colonnes <association_name>_id
Et <association_name>_type
À la table. En l'état, lorsque build_associable
S'exécute, la valeur de _type
Est nil
.
ÉTAPE 2. Passez et acceptez le type d'enfant
Demandez à votre vue de formulaire d'envoyer le child_type
Avec les données de formulaire typiques, et votre contrôleur doit le permettre dans sa vérification des paramètres forts.
# app/views/polymorph/_form.html.erb
<%= form_for(@polymorph) do |form| %>
# Pass in the child_type - This one has been turned into a chicken!
<%= form.hidden_field(:polymorph_type, value: 'Chicken' %>
...
# Form values for Chicken
<%= form.fields_for(:chicken) do |chicken_form| %>
<%= chicken_form.text_field(:hunger_level) %>
<%= chicken_form.text_field(:poop_level) %>
...etc...
<% end %>
<% end %>
# app/controllers/polymorph_controllers.erb
...
private
def polymorph_params
params.require(:polymorph).permit(:id, :polymorph_id, :polymorph_type)
end
Bien sûr, votre ou vos vues devront gérer les différents types de modèles qui sont "associables", mais cela en démontre un.
J'espère que cela aide quelqu'un là-bas. (Pourquoi avez-vous quand même besoin de poulets polymorphes?)
La réponse ci-dessus est excellente mais ne fonctionne pas avec la configuration indiquée. Cela m'a inspiré et j'ai pu créer une solution de travail:
fonctionne pour créer et mettre à jour
class Job <ActiveRecord::Base
belongs_to :client, :polymorphic=>:true
attr_accessible :client_attributes
accepts_nested_attributes_for :client
def attributes=(attributes = {})
self.client_type = attributes[:client_type]
super
end
def client_attributes=(attributes)
some_client = self.client_type.constantize.find_or_initilize_by_id(self.client_id)
some_client.attributes = attributes
self.client = some_client
end
end