6 Stimmen

Rails 3: nested_form, collection_select, accepts_nested_attributes_for und fields_for

Update: hier beantwortet .

Es gibt viele gute Fragen und Antworten hier und im interweb über immer nested_form, collection_select, accepts_nested_attributes_for und fields_for schön zusammen zu spielen, aber ich bin immer noch ratlos. Vielen Dank im Voraus, wenn Sie mir helfen können.

Ziel: Erzeugen eines neuen isbn-Datensatzes. Eine isbn kann viele Mitwirkende haben. Ich verwende erfolgreich die ryanb nested_form gem um bei Bedarf dynamisch neue Mitwirkungsfelder in einem Formular zu erzeugen. Eines dieser Felder verwendet ein collection_select-Dropdown mit allen Namensdatensätzen in Contributor. Wenn der neue Datensatz erstellt wird, müssen die vielen Mitwirkenden-IDs in die Verbindungstabelle (contributors_isbns) geschrieben werden.

Ich habe Teile dieser Arbeit, aber nur bis zu dem Punkt, wo ich eine einzelne Beitragszahler-ID in den neuen Datensatz in der Tabelle isbns speichern kann. Beim Schreiben von Daten in die Join-Tabelle scheine ich nicht weiterzukommen.

Ich habe drei Modelle. Contributors und Isbns haben eine many to many Beziehung, die ich mit has_many :through realisiert habe. Eine isbn kann viele Mitwirkende haben, und ein Mitwirkender kann viele isbns haben. Sie sind über contributors_isbn verbunden.

isbn.rb

  attr_accessible               :contributor_id
  has_many                      :contributors, :through => :contributors_isbns
  has_many                      :contributors_isbns
  accepts_nested_attributes_for :contributors
  accepts_nested_attributes_for :contributors_isbns

contributor.rb

  attr_accessible               :isbn_id
  has_many                      :contributors_isbns
  has_many                      :isbns, :through => :contributors_isbns
  accepts_nested_attributes_for :isbns

beitragszahler_isbn.rb

  class ContributorsIsbn
  attr_accessible               :isbn_id, :contributor_id
  belongs_to                    :isbn
  belongs_to                    :contributor
  accepts_nested_attributes_for :contributors

In der isbns-Steuerung:

 def new
    @isbn  = Isbn.new
    @title = "Create new ISBN"
    1.times {@isbn.contributors.build}
    @isbn.contributors_isbns.build.build_contributor
  end

(Offensichtlich kann ich mich nicht entscheiden, welche Bauweise ich verwenden soll).

In der Ansicht isbns new.html.erb:

<%= nested_form_for @isbn, :validate => false do |f| %>
<h1>Create new ISBN</h1>
<%= render 'shared/error_messages', :object => f.object %>
<%= render 'fields', :f => f %>
  <div class="actions">
    <%= f.submit "Create" %>
  </div>  

<% end %>

Im Teil _fields eine Version mit einem sehr einfachen text_field:

<%= field_set_tag 'Contributor' do %>
<%= f.link_to_add "Add Additional Contributor", :contributors %>
<li>
<%= f.label 'Contributor Sequence Number' %>
<%= f.text_field :descriptivedetail_contributor_sequencenumber%>
</li>

<%= f.fields_for :contributors_isbns, :validate => false do |contrib| %>
<li>
<%= contrib.label :id, 'contributors_isbns id' %>
<%= contrib.text_field :id %>
</li>
<% end %>

<li>
<%= f.label 'Contributor Role' %>
<%= f.text_field :descriptivedetail_contributor_contributorrole  %>
</li>

<% end %>

Und hier eine ausgefallenere Version, die auch nicht funktioniert:

<%= f.fields_for :contributors_isbns, :validate => false do |contributors| %>
<li>
<%= f.label :personnameinverted, 'Contributor Name' %>
<%= f.collection_select(:contributor_id,  Contributor.all, :id, :personnameinverted ) %>
</li>
<% end %>

Dieser Code verwendet die Antwort von ici . Beide führen zu einem "Missing block"-Fehler in der nested_form_for @isbn Linie.

Nochmals vielen Dank im Voraus.

Aktualisierung: ici sind einige Informationen über das nested_form-Gem, die bei der Suche nach dieser Art von Problem nützlich sein könnten. Und hier ist ein [2009 aber immer noch relevanter Beitrag][4] über accepts_nested_attributes_for.

Update 2: Nun, hier ist eine Sache. Ich habe herumgestochert auf eine reduzierte Version dieses in zwei verschiedenen Modellen, nicht mit collection_select oder has_many durch, aber nur auf eine einfache belongs_to / has_many Zuordnung. Das übergeordnete Modell ist Contract und das untergeordnete Modell ist Istc. Ich konnte nicht einmal einen Datensatz über das verschachtelte Formular erstellen. Nachdem ich jedoch im Stack nachgeschaut und gegoogelt habe, erscheint die Fehlermeldung "Warning. Can't mass-assign protected attributes" habe ich gerade hinzugefügt :istcs_attributes zu meinem :attr_accessible Zeile, und jetzt kann ich Datensätze hinzufügen. Ein ziemlich entscheidendes Stück fehlt, und ein Fall von RTFM, da es genau dort in der gem readme steht. Ich werde später aktualisieren, um zu sehen, ob dies auf die kompliziertere has_many durch Zuordnung funktioniert.

Update 4: [Hier][5] ist ein weiterer nützlicher Beitrag über den Umgang mit der Fehlermeldung "Datensatz ungültig".

Update 5: Kleiner Umweg - Als ich dem Formular dynamisch einen neuen Satz von Feldern hinzufügte, wurde einer der untergeordneten Datensätze erstellt. Ich hatte den Link "Hinzufügen" innerhalb des Bereichs für untergeordnete Formulare. Hier ist das Vorher:

<%= f.fields_for :istcs do |istc_form| %>
<h4> Istc</h4>
<%= istc_form.label "istc name" %>
<%= istc_form.text_field :title_title_text %>
<%= istc_form.link_to_remove "[-] Remove this istc"%>
<%= f.link_to_add "[+] Add an istc", :istcs  %>
<% end %>

und hier ist das Ergebnis:

<%= f.fields_for :istcs do |istc_form| %>
<h4> Istc</h4>
<%= istc_form.label "istc name" %>
<%= istc_form.text_field :title_title_text %>
<%= istc_form.link_to_remove "[-] Remove this istc"%>
<% end %>
<%= f.link_to_add "[+] Add an istc", :istcs  %>

Update 6, nach der Beantwortung:

Oh nein. Der collection_select funktioniert nicht. Es fügt neue Mitwirkende Datensätze, nicht mit einem bestehenden aus dem Mitwirkenden Modell. Jemand anderes hatte dieses Problem auch. Irgendwelche Ideen?

3voto

snowangel Punkte 3367

Hurra! Hier ist der Code, mit dem das alles funktioniert. Etwas langatmig, aber ich wollte nichts auslassen. Meine wichtigsten Erkenntnisse:

  • müssen Sie die untergeordneten Attribute attr_accessible im übergeordneten Modell zugänglich machen

  • Sie müssen die über- und untergeordneten IDs attr_accessible in der Join-Tabelle model machen

  • Es macht das Leben einfacher, wenn Sie mindestens eine untergeordnete Instanz im übergeordneten Controller erstellen.

contributor.rb model

class Contributor < ActiveRecord::Base
  attr_accessible  #nothing relevant 
  has_many :contributors_isbns
  has_many :isbns, :through => :contributors_isbns

isbn.rb-Modell

class Isbn < ActiveRecord::Base
  attr_accessible :contributors_attributes, :contributor_id, :istc_id #etc
  belongs_to  :istc
  has_many   :contributors, :through => :contributors_isbns
  has_many   :contributors_isbns
  accepts_nested_attributes_for :contributors #if you omit this you get a missing block error

contributors_isbn Modell

class ContributorsIsbn < ActiveRecord::Base
  belongs_to :isbn
  belongs_to :contributor
  attr_accessible :isbn_id, :contributor_id

isbn controller

 def new
    @isbn  = Isbn.new
    @title = "Create new ISBN"
    1.times {@isbn.contributors.build}
  end

neu.html.erb

<td class="main">
<%= nested_form_for @isbn, :validate => false do |f| %>
<h1>Create new ISBN</h1>
<%= render 'shared/error_messages', :object => f.object %>
<%= render 'fields', :f => f %>
  <div class="actions">
    <%= f.submit "Create" %>
  </div>  

<% end %>

_fields.html.erb

<%= field_set_tag 'Identifier Details' do %>

<li>
<%= f.label 'Title prefix' %>
<%= f.text_field :descriptivedetail_titledetail_titleelement_titleprefix %>
</li>
<li>
<%= f.label 'Title without prefix' %>
<%= f.text_field :descriptivedetail_titledetail_titleelement_titlewithoutprefix %>
</li>
<li>
<%= f.label 'ISTC' %>
<%= f.collection_select(:istc_id, Istc.all, :id, :title_title_text, :prompt => true) %>
</li>

<% end %>

<%= field_set_tag 'Contributor' do %>
<li>
<%= f.label 'Contributor Sequence Number' %>
<%= f.text_field :descriptivedetail_contributor_sequencenumber%>
</li>

<%= f.fields_for :contributors, :validate => false do |contributor_form| %>
<li>
<%= contributor_form.label :personnameinverted, 'Contributor Name' %>
<%= contributor_form.collection_select(:isbn_id, Contributor.all, :id, :personnameinverted ) %>
</li>
<%= contributor_form.link_to_remove "[-] Remove this contributor"%>
<% end %>
<%= f.link_to_add "[+] Add a contributor", :contributors  %>

<li>
<%= f.label 'Contributor Role' %>
<%= f.text_field :descriptivedetail_contributor_contributorrole  %>
</li>

<% end %>

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X