Hinzufügen von Datensätzen zu has_many: durch Zuordnung in Schienen

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

Wie füge ich dem AgentsModell für hinzu Customer?

Ist das der beste Weg?

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

Das obige funktioniert gut von der Konsole aus, ich weiß jedoch nicht, wie ich dies in der tatsächlichen Anwendung erreichen kann.

Stellen Sie sich vor, für den Kunden wird ein Formular ausgefüllt, das auch house_idals Eingabe dient. Dann mache ich folgendes in meinem Controller?

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

Insgesamt bin ich verwirrt, wie Datensätze in die has_many :throughTabelle aufgenommen werden sollen.

Mike
quelle
In welchem ​​Controller würden Sie die Funktion "create" speichern?
Tobias Kolb

Antworten:

161

Ich denke, Sie können dies einfach tun:

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

Oder wenn Sie ein neues Haus für einen Kunden erstellen:

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

Sie können auch über IDs hinzufügen:

@cust.house_ids << House.find(params[:house_id])
Mischa
quelle
16
Zu Ihrer Information: Sie können das zugehörige Haus nur erstellen, wenn der Elternteil bereits gespeichert ist.
Ricardo Otero
Das muss die eleganteste Lösung für dieses Problem sein, auf das ich gestoßen bin. +1 für dich.
Daniel Bonnell
@ RicardoOtero Ich denke, wir können buildstattdessen verwenden create?
Karan
@Mischa Wie soll ich mit Fehlern umgehen, wenn House.find (params [: house_id]) nill ist? Ich habe den Fehler TypeMismatch erhalten, wenn params [: house_id] null ist. Ich verwende bereits Rettung. aber gibt es einen besseren Weg .. ??
Vishal
1
Ich habe beobachtet, dass die Verwendung des <<Operators in bestimmten Fällen zweimal einfügt. Die createMethode ist also der beste Weg.
Swaps
77

Der beste Weg hängt von Ihren Bedürfnissen ab und davon, was sich am angenehmsten anfühlt. Verwirrung kommt aus Unterschieden Active das Verhalten der newund createMethoden und dem <<Betreiber.

Die newMethode

newfügt keinen Assoziationsdatensatz für Sie hinzu. Sie müssen die Houseund AgentAufzeichnungen selbst erstellen :

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

Beachten Sie, dass @cust.houses.newund House.newpraktisch gleich sind, da Sie den AgentDatensatz in beiden Fällen erstellen müssen .

Der <<Betreiber

Wie Mischa erwähnt, können Sie auch den <<Operator für die Sammlung verwenden. Dadurch wird nur das AgentModell für Sie erstellt. Sie müssen das HouseModell erstellen :

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

Die createMethode

createerstellt beide Houseund AgentDatensätze für Sie, aber Sie müssen das AgentModell finden, wenn Sie dies zu Ihrer Ansicht oder API zurückgeben möchten:

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

Wenn Sie beim Erstellen Ausnahmen auslösen möchten, houseverwenden Sie stattdessen die Bang-Operatoren (z . B. new!und create!).

IAmNaN
quelle
2
Sollte die Zeile stattdessen agent = @cust.houses.find(house.id)lesen agent = @cust.agents.find(house.id)? Die agentVariable in der "neuen Methode" unterscheidet sich von der agentin den letzteren Beispielen. Dies kann zu Verwirrung bei Personen führen, die mit zusätzlichen Attributen in der Verknüpfungstabelle arbeiten.
Vaughan
Können Sie näher
6

Eine andere Möglichkeit, Assoziationen hinzuzufügen, besteht in der Verwendung der Fremdschlüsselspalten:

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

Oder verwenden Sie die genauen Spaltennamen und übergeben Sie die ID des zugeordneten Datensatzes anstelle des Datensatzes.

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