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.


¿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])

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!).


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
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.