accepts_nested_attributes_for met hoort_bij polymorfe

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_typevandaan en hoe wordt de waarde ingesteld? Het maakt deel uit van het onderliggende databaserecord omdat polymorfe associaties de kolommen <association_name>_iden <association_name>_typeaan de tabel toevoegen. Zoals het er nu uitziet, wanneer build_associablewordt uitgevoerd, is de waarde van _typenil.

STAP 2. Geef het type kind door en accepteer het

Laat uw formulierweergave het child_typesamen 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.

Other episodes