Forme imbriquée de rails avec has_many: à travers, comment éditer les attributs du modèle de jointure?

103

Comment éditez-vous les attributs d'un modèle de jointure lors de l'utilisation d'accept_nested_attributes_for?

J'ai 3 modèles: Thèmes et articles rejoints par Linkers

class Topic < ActiveRecord::Base
  has_many :linkers
  has_many :articles, :through => :linkers, :foreign_key => :article_id
  accepts_nested_attributes_for :articles
end
class Article < ActiveRecord::Base
  has_many :linkers
  has_many :topics, :through => :linkers, :foreign_key => :topic_id
end
class Linker < ActiveRecord::Base
  #this is the join model, has extra attributes like "relevance"
  belongs_to :topic
  belongs_to :article
end

Alors quand je construis l'article dans la "nouvelle" action du contrôleur de sujets ...

@topic.articles.build

... et créez le formulaire imbriqué dans topics / new.html.erb ...

<% form_for(@topic) do |topic_form| %>
  ...fields...
  <% topic_form.fields_for :articles do |article_form| %>
    ...fields...

... Rails crée automatiquement l'éditeur de liens, ce qui est génial. Maintenant pour ma question: mon modèle Linker a également des attributs que je souhaite pouvoir modifier via le formulaire "nouveau sujet". Mais l'éditeur de liens créé automatiquement par Rails a des valeurs nulles pour tous ses attributs à l'exception de topic_id et article_id. Comment puis-je mettre des champs pour ces autres attributs de l'éditeur de liens dans le formulaire "nouveau sujet" afin qu'ils ne soient pas nuls?

Arcolye
la source
3
J'essaie de faire la même chose que vous, uniquement dans une action new / create ... Je me demande si vous pourriez partager les actions de votre contrôleur. Je veux créer un Useren Accountutilisant un Relationshipcomme un linker... mais je ne peux pas comprendre à quoi ressemblent les actions de création et de création ... cela vous dérange?
Mohamad

Réponses:

90

J'ai trouvé la réponse. L'astuce était:

@topic.linkers.build.build_article

Cela construit les liens, puis construit l'article pour chaque éditeur de liens. Donc, dans les modèles:
topic.rb a besoin de accepts_nested_attributes_for :linkers
linker.rbaccepts_nested_attributes_for :article

Puis sous la forme:

<%= form_for(@topic) do |topic_form| %>
  ...fields...
  <%= topic_form.fields_for :linkers do |linker_form| %>
    ...linker fields...
    <%= linker_form.fields_for :article do |article_form| %>
      ...article fields...
Arcolye
la source
13
Faites-moi savoir si cela vous a été utile
Arcolye
13
Mise à jour de Rails 3: le formulaire form_for et field_for ont besoin de <% =%> au lieu de <
%%
J'ajouterais des backticks de code autour des deux lignes accept_nested_attributes_for que vous avez ajoutées. J'ai manqué cette information à plusieurs reprises en scannant simplement le code - une fois que je l'ai lu attentivement, j'ai attrapé ce détail manquant et cela a résolu mon problème. Merci!
TJ Schuck
2
Honnêtement, c'est le genre d'exemple complet dont les guides rubyonrails.org ont besoin.
ahnbizcad
La clarté visuelle va vraiment très loin, TJ Schuck.
ahnbizcad
6

Lorsque le formulaire généré par Rails est soumis aux Rails controller#action, le paramsaura une structure similaire à celle-ci (quelques attributs composés ajoutés):

params = {
  "topic" => {
    "name"                => "Ruby on Rails' Nested Attributes",
    "linkers_attributes"  => {
      "0" => {
        "is_active"           => false,
        "article_attributes"  => {
          "title"       => "Deeply Nested Attributes",
          "description" => "How Ruby on Rails implements nested attributes."
        }
      }
    }
  }
}

Remarquez comment linkers_attributesest en fait un zéro indexé Hashavec des Stringclés, et non un Array? Eh bien, c'est parce que les clés de champ de formulaire qui sont envoyées au serveur ressemblent à ceci:

topic[name]
topic[linkers_attributes][0][is_active]
topic[linkers_attributes][0][article_attributes][title]

La création de l'enregistrement est désormais aussi simple que:

TopicController < ApplicationController
  def create
    @topic = Topic.create!(params[:topic])
  end
end
Daniel Doezema
la source
Je ne suis pas sûr, mais je pense que cela a été supposé depuis toujoursaccepts_nested_attributes_for
Arcolye
2
@Arcolye - Trouver cette information sur Internet pour une association comme celle-ci était une telle douleur à l'époque - peut-être que mon google-fu était absent ce jour-là. Je voulais au moins le documenter ici en tant que collègue et je supposais simplement que les rails convertissaient linked_attributes en un tableau, au lieu d'un hachage indexé à zéro. Espérons que cette friandise aide quelqu'un à l'avenir :)
Daniel Doezema
3

Un rapide GOTCHA pour l'utilisation de has_one dans votre solution. Je vais simplement copier-coller la réponse donnée par l'utilisateur KandadaBoggu dans ce fil .


La buildsignature de la méthode est différente pour les associations has_oneet has_many.

class User < ActiveRecord::Base
  has_one :profile
  has_many :messages
end

La syntaxe de construction pour l' has_manyassociation:

user.messages.build

La syntaxe de construction pour l' has_oneassociation:

user.build_profile  # this will work

user.profile.build  # this will throw error

Lisez la documentation de l' has_oneassociation pour plus de détails.

8bithero
la source