como adicionar registros a has_many: por meio de associação em trilhos


94
class Agents << ActiveRecord::Base
  belongs_to :customer
  belongs_to :house
end

class Customer << ActiveRecord::Base
  has_many :agents
  has_many :houses, through: :agents
end

class House << ActiveRecord::Base
  has_many :agents
  has_many :customers, through: :agents
end

Como faço para adicionar ao Agentsmodelo para Customer?

É este o melhor caminho?

Customer.find(1).agents.create(customer_id: 1, house_id: 1)

O procedimento acima funciona bem no console, porém, não sei como fazer isso no aplicativo real.

Imagine que um formulário é preenchido para o cliente e também recebe house_idcomo entrada. Então eu faço o seguinte no meu controlador?

def create 
  @customer = Customer.new(params[:customer])
  @customer.agents.create(customer_id: @customer.id, house_id: params[:house_id])
  @customer.save
end

No geral, estou confuso sobre como adicionar registros na has_many :throughtabela?


Em qual controlador você armazenaria a função "criar"?
— Tobias Kolb,

Respostas:


162

Acho que você pode simplesmente fazer isso:

 @cust = Customer.new(params[:customer])
 @cust.houses << House.find(params[:house_id])

Ou ao criar uma nova casa para um cliente:

 @cust = Customer.new(params[:customer])
 @cust.houses.create(params[:house])

Você também pode adicionar via ids:

@cust.house_ids << House.find(params[:house_id])

16
FYI: Você não pode criar a casa associada a menos que o pai já esteja salvo.
— Ricardo Otero

Essa deve ser a solução mais elegante para o problema que encontrei. 1 para você.
— Daniel Bonnell

@RicardoOtero Acho que podemos usar buildistead of create?
— Karan

@Mischa como devo lidar com o erro se House.find (params [: house_id]) for nulo .. Eu obtive o erro de TypeMismatch se params [: house_id] for nulo .. Eu já estou usando o resgate. mas existe algum better_way .. ??
— Vishal

1
Eu observei que usar o <<operador faz a inserção duas vezes em certos casos. Portanto, o createmétodo é o melhor caminho.
— Troca de

77

'A melhor forma' depende das suas necessidades e do que lhe parece mais confortável. Confusão vem de diferenças comportamento de ActiveRecord do newe createmétodos eo <<operador.

O newmétodo

newnão adicionará um registro de associação para você. Você deve construir os registros Housee Agentvocê mesmo:

house = @cust.houses.new(params[:house])
house.save
agent = Agent(customer_id: @cust.id, house_id: house.id)
agent.save

Observe que @cust.houses.newe House.newsão efetivamente os mesmos porque você precisa criar o Agentregistro em ambos os casos.

O <<operador

Como Mischa menciona, você também pode usar o <<operador na coleção. Isso só vai construir o Agentmodelo para você, você deve construir o Housemodelo:

house = House.create(params[:house])
@cust.houses << house
agent = @cust.houses.find(house.id)

O createmétodo

createcriará os registros Housee Agentpara você, mas você precisará encontrar o Agentmodelo se pretende retorná-lo à sua visualização ou api:

house = @cust.houses.create(params[:house])
agent = @cust.agents.where(house: house.id).first

Como nota final, se você quiser que exceções sejam levantadas durante a criação, houseuse os operadores de explosão (por exemplo, new!e create!).


2
A linha deveria ser agent = @cust.houses.find(house.id)lida em agent = @cust.agents.find(house.id)vez disso? A agentvariável no "novo método" é diferente da agentdos últimos exemplos. Pode criar alguma confusão para as pessoas que trabalham com atributos adicionais na tabela de junção.
— vaughan,

você pode elaborar sobre como recuperar dados da tabela conjunta Agentes sem ter N + 1 exemplo de bug exibindo todas as casas e os agentes correspondentes para um determinado cliente
— Ankita.P

6

Outra maneira de adicionar associações é usando as colunas de chave estrangeira:

agent = Agent.new(...)
agent.house = House.find(...)
agent.customer = Customer.find(...)
agent.save

Ou use os nomes exatos das colunas, passando o ID do registro associado em vez do registro.

agent.house_id = house.id
agent.customer_id = customer.id
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.