як додати записи до has_many: через об'єднання в рейки


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

Як додати до Agentsмоделі для Customer?

Це найкращий спосіб?

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

Вище працює чудово з консолі, однак, я не знаю, як цього досягти у реальному додатку.

Уявіть, що заповнена форма для замовника, яка також береться house_idза введення. Тоді я можу зробити наступне у своєму контролері?

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

Загалом мене бентежить, як додати записи в has_many :throughтаблицю?


В якому контролері ви б зберігали функцію "створити"?
Тобіас Колб,

Відповіді:


163

Я думаю, ви можете просто зробити це:

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

Або при створенні нового будинку для замовника:

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

Ви також можете додати через ідентифікатори:

@cust.house_ids << House.find(params[:house_id])

16
FYI: Ви не можете створити відповідний будинок, якщо батько вже не збережений.
Рікардо Отеро,

Це повинно бути найелегантнішим рішенням цієї проблеми, з якою я стикався. +1 для вас.
Даніель Боннелл

@RicardoOtero Я думаю, ми можемо використовувати buildistead від create?
Каран

@Mischa, як мені обробляти помилку, якщо House.find (params [: house_id]) рівна нулю .. я отримав помилку TypeMismatch, якщо params [: house_id] дорівнює нулю .. я вже використовую порятунок. але чи є кращий_ шлях .. ??
Vishal

1
Я зауважив, що використання <<оператора робить вставку двічі у певних випадках. Тож createметод - найкращий спосіб.
Поміняється

77

"Найкращий спосіб" залежить від ваших потреб і того, що вам здається найкомфортнішим. Плутанина відбувається від відмінності поведінки ActiveRecord портретованого , newі createметодів і <<оператора.

newметод

newне додасть для вас запис асоціації. Ви повинні самостійно створити Houseі Agentзаписи:

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

Зауважте, що @cust.houses.newі House.newфактично однакові, оскільки вам потрібно створити Agentзапис в обох випадках.

<<оператор

Як згадує Міша, ви також можете використовувати <<оператор для колекції. Це лише створить Agentмодель для вас, ви повинні побудувати Houseмодель:

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

createметод

createпобудує для вас Houseі Agentзаписи, і вам потрібно буде знайти Agentмодель, якщо ви маєте намір повернути її до свого подання або API:

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

Як останнє зауваження, якщо ви хочете, щоб під час створення houseвиникали винятки, використовуйте замість них оператори bang (наприклад, new!та create!).


2
Чи слід agent = @cust.houses.find(house.id)читати рядок agent = @cust.agents.find(house.id)замість цього? agentЗмінної в «новому методі» відрізняється від agentв останніх прикладах. Може створити певну плутанину у людей, які працюють з додатковими атрибутами на таблиці об’єднання.
воган

чи можете Ви детальніше отримати дані зі спільної таблиці Агенти, не маючи прикладу помилки N + 1, що відображає всі будинки та відповідні агенти для даного клієнта
Ankita.P

6

Іншим способом додавання асоціацій є використання стовпців зовнішнього ключа:

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

Або використовуйте точні імена стовпців, передаючи замість запису ідентифікатор відповідного запису.

agent.house_id = house.id
agent.customer_id = customer.id
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.