Rails принадлежит нескольким моделям с accept_nested_attributes_for

Я создаю систему выставления счетов для продавца автомобилей, в которой каждый счет связан с одним клиентом и одним транспортным средством, при этом у клиентов может быть много счетов, а у транспортных средств также много счетов. У меня он работает с одной вложенной моделью, делая следующее:

purchase_invoice.rb

class PurchaseInvoice < ApplicationRecord
  belongs_to :vehicle
  accepts_nested_attributes_for :vehicle
end

vehicle.rb

class Vehicle < ApplicationRecord
  has_many :purchase_invoices
end

purchase_invoices_controller.rb

def new
  @vehicle = Vehicle.new
  @purchase_invoice = @vehicle.purchase_invoices.build
end

def create
  @vehicle = Vehicle.new
  @purchase_invoice = @vehicle.purchase_invoices.build(invoice_params)

  if @purchase_invoice.save
    redirect_to @purchase_invoice
  else
    render 'new'
  end
end

private

def invoice_params
  params.require(:purchase_invoice).permit(:buyer, :location, :vehicle_price, :transfer_fee, :balance_due, :payment_cash, :payment_bank_transfer, :payment_comment, :status, vehicle_attributes: [:vrm, :date_first_registered, :make, :model, :colour, :transmission, :vin, :fuel, :power])
end

новый.html.erb

<%= form_with model: @purchase_invoice, local: true do |form| %>
  <%= form.fields_for @vehicle do |vehicle_form| %>
  <% end %>
<% end %>

Однако, когда я добавляю второе отношение, подобное этому:

purchase_invoice.rb

class PurchaseInvoice < ApplicationRecord
  belongs_to :customer
  belongs_to :vehicle
  accepts_nested_attributes_for :customer
  accepts_nested_attributes_for :vehicle
end

Я получаю сообщение об ошибке: «Недопустимые параметры: транспортное средство».

Кто-нибудь знает, почему? Кроме того, как мне изменить новое/создать действие контроллера для сборки, сохраняя при этом сильные параметры?

Я гуглил это уже четыре часа и много пробовал, но не повезло. Заранее спасибо всем!

Обновить

Вот мои журналы:

Started POST "/purchase_invoices" for 127.0.0.1 at 2018-03-20 15:10:01 +0000
Processing by PurchaseInvoicesController#create as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"JB8py9zNxew6aQ6/za3JHDEb4j8f9HGujTlS6P1Eyhb+5NtPPP47fW7AHBkt9eURcnXg0gh9Mf1DCKCSwvlAbg==", "purchase_invoice"=>{"customer"=>{"name"=>""}, "vehicle"=>{"vrm"=>"SA07SSX", "make"=>"VAUXHALL", "model"=>"MERIVA DESIGN", "colour"=>"Silver", "vin"=>"W0L0XCE7574216645", "date_first_registered"=>"20/03/2007"}, "vehicle_odomoter_reading"=>"", "vehicle_number_of_keys"=>"", "vehicle_mot_expiry"=>"", "vehicle_hpi_clear"=>"", "vehicle_comments"=>"", "buyer"=>"", "location"=>"", "vehicle_price"=>"", "transfer_fee"=>"0", "balance_due"=>"", "payment_cash"=>"", "payment_bank_transfer"=>"", "payment_comments"=>""}, "commit"=>"Create Purchase invoice"}
  Vehicle Load (0.3ms)  SELECT  "vehicles".* FROM "vehicles" WHERE "vehicles"."vrm" = ? ORDER BY "vehicles"."id" ASC LIMIT ?  [["vrm", "SA07SSX"], ["LIMIT", 1]]
Unpermitted parameters: :customer, :vehicle, :payment_comments
    (0.1ms)  begin transaction
    (0.1ms)  rollback transaction
  Rendering purchase_invoices/new.html.erb within layouts/application
  Rendered purchase_invoices/new.html.erb within layouts/application (10.2ms)
  Rendered layouts/_header.html.erb (1.7ms)
Completed 200 OK in 63ms (Views: 54.4ms | ActiveRecord: 0.4ms)

person Kurtis Fehr    schedule 20.03.2018    source источник
comment
Можете ли вы добавить журналы сервера, где вы получаете ошибку? Моя первоначальная мысль заключалась в том, что вы передаете параметр vehicle, а не вложенный атрибут транспортного средства, что вы разрешаете в параметрах с помощью этой строки vehicle_attributes: [:vrm, :date_first_registered, :make, :model, :colour, :transmission, :vin, :fuel, :power]   -  person gwalshington    schedule 20.03.2018
comment
Эй, спасибо за ваш быстрый ответ! Я обновил его выше.   -  person Kurtis Fehr    schedule 20.03.2018
comment
Можете ли вы опубликовать все это, пожалуйста? Будет проще, если вы опубликуете все соответствующие строки - ошибку, параметры и т. д. Спасибо!!   -  person gwalshington    schedule 20.03.2018
comment
Вложенные атрибуты IIRC предназначены для работы по принципу «родитель-потомок». У вас есть ребенок -> родитель.   -  person DickieBoy    schedule 20.03.2018
comment
Обновлен @gwalshington.   -  person Kurtis Fehr    schedule 20.03.2018
comment
Привет, @DickieBoy, спасибо за ответ. Я действительно удивился этому, но осмотрелся и прочитал, что он был добавлен в Rails 4. Кроме того, он работает с транспортным средством, но не с транспортным средством и с клиентом. Меня не слишком беспокоит, как это написано, мне просто нужен счет-фактура, чтобы использовать как транспортные средства, так и таблицу клиентов.   -  person Kurtis Fehr    schedule 20.03.2018
comment
Кроме того, @gwalshington на всякий случай, если вам интересно, причина, по которой он выбрал транспортное средство выше, заключается в том, что я сначала проверяю транспортное средство, а не делаю new() каждый раз. Это потому, что я не хочу дублировать автомобили каждый раз, когда создается счет, и предпочел бы использовать существующий автомобиль, если он доступен.   -  person Kurtis Fehr    schedule 20.03.2018
comment
Все неразрешенные параметры пишутся не так, как вы их разрешаете. Либо добавляется attributes, либо имеет место множественное число. Я бы посмотрел на разницу между тем, что разрешают ваши параметры, и тем, что вы отправляете. Кроме того, как упоминалось выше, у вас могут возникнуть проблемы с их ассоциациями.   -  person gwalshington    schedule 20.03.2018
comment
Дополнительно - если я могу дать несколько советов. Как правило, вы хотите сделать PurchaseInvoice таблицу соединений и сохранить id автомобиля и покупателя при покупке, а не их атрибуты.   -  person gwalshington    schedule 20.03.2018
comment
@gwalshington Я не думал, что объединенная таблица будет уместной, но у меня нет лучшего понимания, так что, может быть, вы можете уточнить? PurchaseInvoice имеет несколько соответствующих полей, а также идентификатор клиента и идентификатор транспортного средства. Я полагал, что это будет отношение has_many и own_to, поскольку счет-фактура принадлежит одному Клиенту и одному Транспортному средству, тогда у одного Клиента может быть много счетов-фактур, а у одного Транспортного средства может быть много счетов-фактур. Я что-то упускаю?   -  person Kurtis Fehr    schedule 20.03.2018
comment
@KurtisFehr Я думаю, что это можно сделать, но это не делается как вложенные атрибуты в традиционном смысле. Попробуйте удалить _attributes из разрешенных параметров.   -  person DickieBoy    schedule 20.03.2018
comment
@DickieBoy, к сожалению, не повезло. Могу я спросить, как бы вы подошли к этому, если бы вы этим занимались? Какой тип отношений вы бы сделали? Я пришел к выводу, что отношение has_many/belongs_to было лучшим, потому что Транспортное средство и Клиент могли иметь много счетов, но каждый счет принадлежал одному Транспортному средству и одному Клиенту. Я не думаю, что это соединение или полиморф — правильно ли я думаю? Большое спасибо, кстати.   -  person Kurtis Fehr    schedule 20.03.2018


Ответы (1)


У вашего подхода есть некоторые проблемы, но вы очень близки. Модель PurchaseInvoice принадлежит транспортному средству и покупателю и хранит customer_id и vehicle_id. . Это правильно. Как вы сказали в комментарии, это гораздо больше, чем просто модель соединения, потому что она содержит множество других данных, таких как цена, комиссия за перевод и т. д. В любом случае, вы передаете множество параметров, касающихся транспортного средства для покупки, а не id транспортного средства. На самом деле вы создаете транспортное средство в Счете-фактуре на покупку, что не имеет смысла. Более того, ваши vehicle_attributes должны быть не массивом, а хэшем (поскольку PurchaseInvoice belongs_to :vehicle; поэтому он всего один) и должен иметь только vehicle_id. А учитывая, что вам нужен только vehicle_id, вам не нужен nested_attributes (ни для клиента) . Я бы поменял:

Контроллер:

def new
  @vehicles = Vehicle.all   #All vehicles, to select from the list
  @customers = Customer.all #All customers, to select from the list (unless you use current_user)
  @purchase_invoice = PurchaseInvoice.new
end

def create
  @vehicle = Vehicle.find(params[:vehicle_id])
  @customer = Customer.find(params[:customer_id]) # Or use current_user
  #The above is only needed to check if vehicle and customer exist, but it is not needed below

  @purchase_invoice = PurchaseInvoice.create(invoice_params)

  if @purchase_invoice.save
    redirect_to @purchase_invoice
  else
    render 'new'
  end
end

def invoice_params
  params.require(:purchase_invoice).permit(:customer_id, vehicle_id, :location, :vehicle_price, :transfer_fee, :balance_due, :payment_cash, :payment_bank_transfer, :payment_comment, :status)
end

Вид:

<%= form_with model: @purchase_invoice, local: true do |form| %>

  <!-- This is necessary to choose the customer. If the customer is current_user, just remove it. -->
  <%= form.collection_select(:customer_id, @customers, :id, :name %>

  <!-- This is necessary to choose the vehicle. This is extremely simplified -->
  <!-- The customer should be able to look at many attributes of the car -->
  <!-- to be able to select one to purchase -->
  <%= form.collection_select(:vehicle_id, @vehicles, :id, :make %>

  <!-- All other invoice fields. -->
<% end %>
person Pablo    schedule 21.03.2018
comment
Спасибо, я понял это отсюда. Хорошего дня! - person Kurtis Fehr; 21.03.2018