cara menambahkan catatan ke has_many: melalui pengaitan di rel

97
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

Bagaimana cara menambahkan Agentsmodel Customer?

Apakah ini cara terbaik?

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

Di atas berfungsi dengan baik dari konsol namun, saya tidak tahu bagaimana mencapai ini dalam aplikasi sebenarnya.

Bayangkan sebuah formulir diisi untuk pelanggan yang juga mengambil house_idmasukan. Lalu apakah saya melakukan hal berikut di pengontrol saya?

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

Secara keseluruhan saya bingung bagaimana cara menambahkan catatan di has_many :throughtabel?

Mike
sumber
Di pengontrol mana Anda akan menyimpan fungsi "create"?
Tobias Kolb

Jawaban:

166

Saya pikir Anda cukup melakukan ini:

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

Atau saat membuat rumah baru untuk pelanggan:

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

Anda juga dapat menambahkan melalui id:

@cust.house_ids << House.find(params[:house_id])
Mischa
sumber
16
FYI: Anda tidak dapat membuat rumah terkait kecuali orang tuanya sudah disimpan.
Ricardo Otero
Itu harus menjadi solusi paling elegan untuk masalah ini yang pernah saya temui. 1 untuk Anda.
Daniel Bonnell
@RicardoOtero Kurasa kita bisa menggunakan yang buildpertama create?
Karan
@Mischa bagaimana cara menangani error jika House.find (params [: house_id]) nill .. saya mendapat error TypeMismatch jika params [: house_id] nihil .. saya sudah menggunakan penyelamatan. tapi apakah ada cara yang lebih baik .. ??
Vishal
1
Saya telah mengamati bahwa menggunakan <<operator melakukan penyisipan dua kali dalam kasus tertentu. Jadi createmetode adalah cara terbaik.
Tukar
78

'Cara terbaik' tergantung pada kebutuhan Anda dan apa yang dirasa paling nyaman. Kebingungan berasal dari perbedaan perilaku ActiveRecord ini dari newdan createmetode dan <<operator.

The newMetode

newtidak akan menambahkan catatan asosiasi untuk Anda. Anda harus membuat Housedan Agentmencatat sendiri:

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

Perhatikan bahwa @cust.houses.newdan House.newsecara efektif sama karena Anda perlu membuat Agentrekaman dalam kedua kasus.

The <<Operator

Seperti yang disebutkan Mischa, Anda juga bisa menggunakan <<operator pada collection. Ini hanya akan membangun Agentmodel untuk Anda, Anda harus membangun Housemodel:

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

The createMetode

createakan membangun keduanya Housedan Agentrecord untuk Anda, tetapi Anda perlu menemukan Agentmodelnya jika Anda ingin mengembalikannya ke view atau api:

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

Sebagai catatan terakhir, jika Anda ingin pengecualian dimunculkan saat membuat housegunakan operator bang sebagai gantinya (misalnya new!dan create!).

IAmNaN
sumber
2
Haruskah baris itu agent = @cust.houses.find(house.id)dibaca agent = @cust.agents.find(house.id)? The agentvariabel dalam "Metode baru" ini berbeda dengan agentpada contoh terakhir. Mungkin membuat kebingungan bagi orang yang bekerja dengan atribut tambahan di tabel gabungan.
vaughan
dapatkah Anda menguraikan tentang mengambil data dari tabel gabungan Agen tanpa contoh bug N + 1 yang menampilkan semua rumah dan agen terkait untuk pelanggan tertentu
Ankita.P
6

Cara lain untuk menambahkan asosiasi adalah dengan menggunakan kolom kunci asing:

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

Atau gunakan nama kolom yang tepat, dengan meneruskan ID dari record terkait, bukan record.

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