Rails 5 вставляет вместо обновления при изменении значения атрибута в таблице соединений - PullRequest
2 голосов
/ 07 мая 2020

У меня есть отношение HABTM с использованием ассоциации has_many: through. У меня возникли проблемы с обновлением атрибута в таблице соединений, потому что вместо обновления записи он просто вставляет новую запись в таблицу, создавая дубликаты.

Я пробовал использовать ограничение UNIQUE при создании индекса и добавлении проверки, но теперь, когда я обновляю запись, я получаю ошибку проверки или ошибку: ActiveRecord :: RecordNotUnique в BatchesController # update Mysql2: : Ошибка: повторяющаяся запись

Чтобы предоставить некоторый контекст:

  • У меня есть 4 таблицы: manufacturing_orders, order_products, batch и batches_order_products (таблица соединений).
  • У production_order много order_products.
  • Также у production_order много партий.
  • В партиях много order_products.
  • Когда я создаю партию, я копирую все order_products, принадлежащие одному производственному_order, и в форме я могу назначить количество для каждого из них.

Таким образом, создание выглядит нормально, но когда я обновляю любое количество, оно просто снова вставляет все отношение вместо обновления существующего.

Модель production_order.rb :

class ManufacturingOrder < ApplicationRecord
  has_many :batches, inverse_of: :manufacturing_order, dependent: :destroy
  has_many :order_products, inverse_of: :manufacturing_order, dependent: :destroy

  accepts_nested_attributes_for :order_products
  accepts_nested_attributes_for :batches
end

Модель order_product.rb :

class OrderProduct < ApplicationRecord

  belongs_to :manufacturing_order

  has_many :batches_order_products
  has_many :batches, :through => :batches_order_products
end

Модель batch.rb :

class Batch < ApplicationRecord
  belongs_to :manufacturing_order

  has_many :batches_order_products
  has_many :order_products, :through => :batches_order_products

  accepts_nested_attributes_for :batches_order_products
end

Модель batches_order_product.rb :

class BatchesOrderProduct < ApplicationRecord
  belongs_to :batch
  belongs_to :order_product

  validates :batch_id, uniqueness: { scope: :order_product_id }
end

Контроллер batches_controller.rb :

class BatchesController < ApplicationController
  def new
    manufacturing_order = ManufacturingOrder.find(params[:manufacturing_order_id])
    order_products = manufacturing_order.order_products
    @batch = Batch.new({
      manufacturing_order: manufacturing_order,
      order_products: order_products
    })
  end

  def create
    @batch = Batch.new(load_params)
    if @batch.save
      flash[:notice] = crud_success
      redirect_to action: :index
    else
      flash[:error] = @batch.errors.full_messages.to_sentence
      render action: :new
    end
  end

  def edit
    @batch = Batch.find(params[:id])
  end

  def update
    @batch = Batch.find(params[:id])
    if @batch.update_attributes(load_params)
      flash[:notice] = crud_success
      redirect_to action: :index
    else
      flash[:error] = @batch.errors.full_messages.to_sentence
      render action: :edit
    end
  end

  private
  def load_params
    params.require(:batch)
    .permit(:name,
      :date,
      :manufacturing_order_id,
      :status,
      order_products: [],
      order_products_ids: [],
      batches_order_products_attributes: [:id, :quantity, :order_product_id]
      )
  end
end

Это форма в партиях:

  = bootstrap_form_for([@batch.manufacturing_order, @batch]) do |f|
    = f.hidden_field :manufacturing_order_id
       = f.text_field :name, label: 'Name'
        = f.text_field :date
    table
      thead
        tr
          th= "Product"
          th= "Quantity"
    tbody
      = f.fields_for :batches_order_products do |bop|
        = bop.hidden_field :order_product_id
      tr
        td
          = bop.object.order_product.name
        td
          = bop.text_field :quantity

  = f.submit 'Save'

Любая помощь будет очень признательна. Спасибо!

ОБНОВЛЕНИЕ: это параметры, переданные при отправке формы редактирования. Есть подсказка?

{"utf8"=>"✓",
 "_method"=>"patch",
 "batch"=>
  {"manufacturing_order_id"=>"8",
   "name"=>"MAS",
   "date"=>"07/05/2020",
   "batches_order_products_attributes"=>
    {"0"=>{"order_product_id"=>"12", "quantity"=>"77777777", "id"=>""},
     "1"=>{"order_product_id"=>"13", "quantity"=>"9.0", "id"=>""},
     "2"=>{"order_product_id"=>"14", "quantity"=>"7.0", "id"=>""}}},
 "commit"=>"Guardar",
 "manufacturing_order_id"=>"8",
 "id"=>"7"}

РЕДАКТИРОВАТЬ 2: Я обновил вложенную форму, включив идентификатор в скрытое поле, например:

= f.fields_for :batches_order_products do |bop|
  = bop.hidden_field :order_product_id
  = bop.hidden_field :id, value: @batch.id

  = bop.object.order_product.name
  = bop.text_field :quantity, label: ''

НО теперь Rails жалуется на это при обновлении:

ActiveRecord::StatementInvalid in BatchesController#update

Mysql2::Error: Unknown column 'batches_order_products.' in 'where clause': SELECT `batches_order_products`.* FROM `batches_order_products` WHERE `batches_order_products`.`batch_id` = 9 AND `batches_order_products`.`` IN ('9', '9', '9', '9', '9') 

Я не знаю, почему Rails добавляет эту последнюю странную часть в запрос SQL.

1 Ответ

0 голосов
/ 07 мая 2020

Итак, я наконец понял это.

Проблема заключалась в том, что объединенной таблице требовался столбец идентификатора для ссылки. У таблицы был индекс batch_id и order_product_id, но по какой-то причине он не работал, и ActiveRecord искал идентификатор. Его добавление решило проблему.

Спасибо @max за то, что он дал несколько очков.

class AddIndexToBatchesOrderProductsJoinTable < ActiveRecord::Migration[5.2]
  def change
    # add_index :batches_order_products, [:batch_id, :order_product_id], unique: true
    add_column :batches_order_products, :id, :primary_key
  end
end
...