Forma anidada de rieles con has_many: a través, ¿cómo editar los atributos del modelo de unión?

103

¿Cómo se editan los atributos de un modelo de combinación cuando se utiliza accept_nested_attributes_for?

Tengo 3 modelos: Temas y artículos unidos por vinculadores

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

Entonces, cuando construyo el artículo en la acción "nueva" del controlador de temas ...

@topic.articles.build

... y crea el formulario anidado en topics / new.html.erb ...

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

... Rails crea automáticamente el enlazador, lo cual es genial. Ahora para mi pregunta: Mi modelo de Linker también tiene atributos que quiero poder cambiar a través del formulario de "nuevo tema". Pero el enlazador que Rails crea automáticamente tiene valores nulos para todos sus atributos, excepto topic_id y article_id. ¿Cómo puedo poner campos para esos otros atributos del enlazador en el formulario "nuevo tema" para que no salgan nulos?

Arcolye
fuente
3
Estoy tratando de hacer lo mismo que tú, solo que en una acción nueva / crear ... Me pregunto si podrías compartir las acciones de tu controlador. Quiero crear un a Usertravés de un Accountuso de un Relationshipcomo linker... pero no puedo entender cómo deben verse las acciones nuevas y de creación ... ¿te importaría?
Mohamad

Respuestas:

90

Descubrí la respuesta. El truco fue:

@topic.linkers.build.build_article

Eso crea los enlazadores y luego crea el artículo para cada enlazador. Entonces, en los modelos:
topic.rb necesita accepts_nested_attributes_for :linkers
linker.rb necesitaaccepts_nested_attributes_for :article

Luego en la forma:

<%= 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
fuente
13
Hágame
13
Actualización de Rails 3: form_for y field_for necesitan <% =%> en lugar de <
%%
Agregaría comillas invertidas de código alrededor de las dos accept_nested_attributes_for líneas que agregó. Me perdí esa información repetidamente mientras escaneaba el código; una vez que lo leí detenidamente, capté ese detalle perdido y resolvió mi problema. ¡Gracias!
TJ Schuck
2
Honestamente, este es el tipo de ejemplo completo que necesitan las guías de rubyonrails.org.
ahnbizcad
La claridad visual realmente recorre un largo camino, TJ Schuck.
ahnbizcad
6

Cuando el formulario generado por Rails se envía a Rails controller#action, paramstendrá una estructura similar a esta (se agregaron algunos atributos inventados):

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."
        }
      }
    }
  }
}

Observe cómo en linkers_attributesrealidad un índice cero Hashcon Stringclaves y no un Array? Bueno, esto se debe a que las claves de campo de formulario que se envían al servidor tienen este aspecto:

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

Crear el registro ahora es tan simple como:

TopicController < ApplicationController
  def create
    @topic = Topic.create!(params[:topic])
  end
end
Daniel Doezema
fuente
No estoy seguro, pero creo que eso se ha asumido todo el tiempoaccepts_nested_attributes_for
Arcolye
2
@Arcolye - Encontrar esta información en Internet para una asociación como esta fue una molestia en ese momento; tal vez mi google-fu estaba inactivo ese día. Quería al menos documentarlo aquí, ya que yo, como mi compañero de trabajo, asumí que los rieles convertían los atributos enlazados en una matriz, en lugar de un hash indexado cero. Espero que este dato ayude a alguien en el futuro :)
Daniel Doezema
3

Un GOTCHA rápido para cuando use has_one en su solución. Simplemente copiaré y pegaré la respuesta dada por el usuario KandadaBoggu en este hilo .


La buildfirma del método es diferente para has_oney has_manyasociaciones.

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

La sintaxis de compilación para la has_manyasociación:

user.messages.build

La sintaxis de compilación para la has_oneasociación:

user.build_profile  # this will work

user.profile.build  # this will throw error

Leer la has_oneasociación documentación de para obtener más detalles.

8bithero
fuente