Forma aninhada de Rails com has_many: através, como editar atributos do modelo de junção?

103

Como você edita os atributos de um modelo de junção ao usar aceita_nested_attributes_for?

Eu tenho 3 modelos: tópicos e artigos unidos por 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

Então, quando eu construo o artigo na "nova" ação do controlador de tópicos ...

@topic.articles.build

... e crie o formulário aninhado em topics / new.html.erb ...

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

... O Rails cria automaticamente o linker, o que é ótimo. Agora, minha pergunta: Meu modelo de vinculador também tem atributos que desejo poder alterar por meio do formulário "novo tópico". Mas o linker que o Rails cria automaticamente tem valores nulos para todos os seus atributos, exceto topic_id e article_id. Como posso colocar campos para esses outros atributos de vinculador no formulário "novo tópico" para que eles não saiam nulo?

Arcolye
fonte
3
Estou tentando fazer a mesma coisa que você, apenas em uma ação new / create ... Gostaria de saber se você poderia compartilhar suas ações de controlador. Quero criar um Userpor meio de um Accountusando um Relationshipcomo linker... mas não consigo descobrir como as ações new e create devem ser ... você se importaria?
Mohamad

Respostas:

90

Descobri a resposta. O truque era:

@topic.linkers.build.build_article

Isso cria os vinculadores e, em seguida, o artigo para cada vinculador. Portanto, nos modelos:
topic.rb needs accepts_nested_attributes_for :linkers
linker.rb needsaccepts_nested_attributes_for :article

Depois, na 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
fonte
13
Informe se isso foi útil
Arcolye
13
Atualização do Rails 3: o form_for e o field_for precisam de <% =%> em vez de <
%%
Eu adicionaria crases de código em torno das duas linhas accept_nested_attributes_for que você adicionou. Perdi essa informação repetidamente enquanto apenas digitalizava o código - depois de lê-lo completamente, percebi o detalhe perdido e isso resolveu meu problema. Obrigado!
TJ Schuck
2
honestamente, esse é o tipo de exemplo completo de que os guias do rubyonrails.org precisam.
ahnbizcad de
A clareza visual realmente ajuda muito, TJ Schuck.
ahnbizcad de
6

Quando o formulário gerado pelo Rails for submetido ao Rails controller#action, o paramsterá uma estrutura semelhante a esta (alguns atributos compostos adicionados):

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 como, linkers_attributesna verdade, um zero é indexado Hashcom Stringchaves, e não um Array? Bem, isso ocorre porque as chaves de campo do formulário que são enviadas ao servidor se parecem com estas:

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

Criar o registro agora é tão simples quanto:

TopicController < ApplicationController
  def create
    @topic = Topic.create!(params[:topic])
  end
end
Daniel Doezema
fonte
Não tenho certeza, mas acho que isso foi assumido o tempo todoaccepts_nested_attributes_for
Arcolye
2
@Arcolye - Encontrar essas informações na internet para uma associação como essa foi uma dor na época - talvez meu google-fu estivesse desligado naquele dia. Eu queria pelo menos documentá-lo aqui, pois eu, como meu colega de trabalho, e apenas assumi que rails converteu linked_attributes em uma matriz, em vez de um hash com índice zero. Espero que este petisco ajude alguém no futuro :)
Daniel Doezema
3

Um GOTCHA rápido para quando usar has_one em sua solução. Vou apenas copiar e colar a resposta dada pelo usuário KandadaBoggu em esta discussão .


A buildassinatura do método é diferente para associações has_onee has_many.

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

A sintaxe de construção para has_manyassociação:

user.messages.build

A sintaxe de construção para has_oneassociação:

user.build_profile  # this will work

user.profile.build  # this will throw error

Leia a documentação da has_oneassociação para mais detalhes.

8bithero
fonte