comment ajouter des enregistrements à has_many: par association dans les rails

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

Comment puis-je ajouter au Agentsmodèle pour Customer?

Est-ce le meilleur moyen?

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

Ce qui précède fonctionne bien depuis la console, cependant, je ne sais pas comment y parvenir dans l'application réelle.

Imaginez qu'un formulaire soit rempli pour le client qui soit également pris house_iden compte. Puis-je faire ce qui suit dans mon contrôleur?

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

Dans l'ensemble, je ne sais pas comment ajouter des enregistrements dans le has_many :throughtableau?

Mike
la source
Dans quel contrôleur stockeriez-vous la fonction «créer»?
Tobias Kolb

Réponses:

163

Je pense que vous pouvez simplement faire ceci:

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

Ou lors de la création d'une nouvelle maison pour un client:

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

Vous pouvez également ajouter via des identifiants:

@cust.house_ids << House.find(params[:house_id])
Mischa
la source
16
FYI: Vous ne pouvez pas créer la maison associée à moins que le parent ne soit déjà enregistré.
Ricardo Otero
Cela doit être la solution la plus élégante à ce problème que j'ai rencontré. +1 pour vous.
Daniel Bonnell
@RicardoOtero Je suppose que nous pouvons utiliser buildistead of create?
Karan
@Mischa comment dois-je gérer l'erreur si House.find (params [: house_id]) est nul .. j'ai une erreur de TypeMismatch si les paramètres [: house_id] sont nil .. j'utilise déjà le sauvetage. mais y a-t-il une meilleure façon .. ??
Vishal
1
J'ai observé que l'utilisation de l' <<opérateur effectue l'insertion deux fois dans certains cas. La createméthode est donc la meilleure.
Echange le
77

«La meilleure façon» dépend de vos besoins et de ce qui vous convient le mieux. La confusion provient des différences de comportement ActiveRecord des newet createméthodes et l' <<opérateur.

La newméthode

newn'ajoutera pas d'enregistrement d'association pour vous. Vous devez créer vous-même les enregistrements Houseet Agent:

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

Notez que @cust.houses.newet House.newsont effectivement les mêmes car vous devez créer l' Agentenregistrement dans les deux cas.

L' <<opérateur

Comme le mentionne Mischa, vous pouvez également utiliser l' <<opérateur sur la collection. Cela ne construira le Agentmodèle que pour vous, vous devez construire le Housemodèle:

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

La createméthode

createconstruira les deux Houseet les Agentenregistrements pour vous, mais vous devrez trouver le Agentmodèle si vous avez l'intention de le renvoyer dans votre vue ou votre API:

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

Pour finir, si vous voulez que des exceptions soient levées lors de la création, houseutilisez plutôt les opérateurs bang (par exemple new!et create!).

IAmNaN
la source
2
La ligne devrait-elle se agent = @cust.houses.find(house.id)lire à la agent = @cust.agents.find(house.id)place? La agentvariable de la "nouvelle méthode" est différente de celle agentdes derniers exemples. Peut créer une certaine confusion pour les personnes travaillant avec des attributs supplémentaires sur la table de jointure.
vaughan
pouvez-vous élaborer sur la récupération des données de la table commune Agents sans avoir N + 1 exemple de bogue affichant toutes les maisons et les agents correspondants pour le client donné
Ankita.P
6

Une autre façon d'ajouter des associations consiste à utiliser les colonnes de clé étrangère:

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

Ou utilisez les noms de colonne exacts, en passant l'ID de l'enregistrement associé au lieu de l'enregistrement.

agent.house_id = house.id
agent.customer_id = customer.id
Dennis
la source