cómo agregar registros a has_many: a través de la asociación en rieles

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

¿Cómo agrego al Agentsmodelo para Customer?

Es esta la mejor manera?

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

Lo anterior funciona bien desde la consola, sin embargo, no sé cómo lograrlo en la aplicación real.

Imagine que se llena un formulario para el cliente que también lo toma house_idcomo entrada. Entonces, ¿hago lo siguiente en mi controlador?

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

En general, estoy confundido sobre cómo agregar registros en la has_many :throughtabla.

Miguel
fuente
¿En qué controlador almacenarías la función "crear"?
Tobias Kolb

Respuestas:

162

Creo que simplemente puedes hacer esto:

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

O al crear una nueva casa para un cliente:

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

También puede agregar a través de identificadores:

@cust.house_ids << House.find(params[:house_id])
Mischa
fuente
16
FYI: No puede crear la casa asociada a menos que el padre ya esté guardado.
Ricardo Otero
Esa tiene que ser la solución más elegante a este problema que he encontrado. +1 para ti.
Daniel Bonnell
@RicardoOtero Supongo que podemos usar buildis en lugar de create?
Karan
@Mischa ¿cómo debo manejar el error si House.find (params [: house_id]) es nulo ... tengo un error de TypeMismatch si params [: house_id] es nulo ... ya estoy usando rescate. pero hay alguna forma mejor ... ??
Vishal
1
He observado que el <<operador usa dos veces la inserción en ciertos casos. Entonces el createmétodo es la mejor manera.
Intercambia
77

"La mejor manera" depende de sus necesidades y de lo que le resulte más cómodo. La confusión proviene de las diferencias de comportamiento de la ActiveRecord newy createmétodos y el <<operador.

El newmétodo

newno agregará un registro de asociación por usted. Tienes que crear los registros Housey Agenttú mismo:

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

Tenga en cuenta que @cust.houses.newy House.newson efectivamente iguales porque necesita crear el Agentregistro en ambos casos.

El <<operador

Como menciona Mischa, también puede usar el <<operador en la colección. Esto solo construirá el Agentmodelo para usted, debe construir el Housemodelo:

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

El createmétodo

createconstruirá ambos Housey Agentregistros por usted, pero deberá encontrar el Agentmodelo si tiene la intención de devolverlo a su vista o api:

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

Como nota final, si desea que se generen excepciones al crear, houseutilice los operadores bang en su lugar (por ejemplo, new!y create!).

IAmNaN
fuente
2
¿Debería agent = @cust.houses.find(house.id)leerse la línea en su agent = @cust.agents.find(house.id)lugar? La agentvariable del "nuevo método" es diferente a la agentde los últimos ejemplos. Puede crear cierta confusión para las personas que trabajan con atributos adicionales en la tabla de combinación.
Vaughan
¿Puede profundizar en la recuperación de datos de la tabla conjunta Agentes sin tener un ejemplo de error N + 1 que muestre todas las casas y los agentes correspondientes para el cliente dado
Ankita.P
6

Otra forma de agregar asociaciones es usando las columnas de clave externa:

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

O utilice los nombres exactos de las columnas, pasando el ID del registro asociado en lugar del registro.

agent.house_id = house.id
agent.customer_id = customer.id
Dennis
fuente