Ik wil graag een polymorfe relatie opzetten met accepts_nested_attributes_for
. Hier is de 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
Als ik probeer Job.create(..., :client_attributes=>{...}
te openen, krijg ik NameError: uninitialized constant Job::Client
Antwoord 1, autoriteit 100%
Ik heb ook een probleem gehad met de “ArgumentError: Cannot build association model_name. Probeert u een polymorfe één-op-één-koppeling op te bouwen?”
En ik heb een betere oplossing gevonden voor dit soort problemen. U kunt de native methode gebruiken. Laten we eens kijken naar de implementatie van nested_attributes, binnen 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
Dus wat moeten we hier eigenlijk doen? Is gewoon om build_#{association_name} in ons model te maken. Ik heb een volledig werkend voorbeeld onderaan gedaan:
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
Antwoord 2, autoriteit 35%
Ik eindelijkheb dit werkend gekregen met Rails 4.x. Dit is gebaseerd op het antwoord van Dmitry/ScotterC, dus geef ze een +1.
STAP 1.Om te beginnen is hier het volledige model met polymorfe associatie:
# 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
Ja, dat is niet echt nieuw. Je vraagt je misschien af, waar komt polymorph_type
vandaan en hoe wordt de waarde ingesteld? Het maakt deel uit van het onderliggende databaserecord omdat polymorfe associaties de kolommen <association_name>_id
en <association_name>_type
aan de tabel toevoegen. Zoals het er nu uitziet, wanneer build_associable
wordt uitgevoerd, is de waarde van _type
nil
.
STAP 2. Geef het type kind door en accepteer het
Laat uw formulierweergave het child_type
samen met de typische formuliergegevens verzenden, en uw controller moet dit toestaan in zijn sterke parametercontrole.
# 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
Natuurlijk zullen uw weergave(n) de verschillende typen modellen moeten kunnen verwerken die ‘associabel’ zijn, maar dit toont er een aan.
Ik hoop dat dit iemand helpt. (Waarom heb je eigenlijk polymorfe kippen nodig?)
Antwoord 3, autoriteit 13%
Het bovenstaande antwoord is geweldig, maar werkt niet met de getoonde setup. Het inspireerde me en ik was in staat om een werkende oplossing te creëren:
werkt voor het maken en bijwerken
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
Antwoord 4, autoriteit 8%
Ik kwam er net achter dat rails dit soort gedrag niet ondersteunt, dus bedacht ik de volgende oplossing:
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
Hierdoor kan ik mijn formulier als volgt instellen:
<%= f.select :client_type %>
<%= f.fields_for :client do |client|%>
<%= client.text_field :name %>
<% end %>
Niet de exacte oplossing, maar het idee is belangrijk.