【问题标题】:Handling join table entries based on association attributes根据关联属性处理连接表条目
【发布时间】:2018-07-27 10:48:54
【问题描述】:

TL;DR

基于具有关联属性(如条形码或车牌号)的表单创建连接表条目的最佳方法是什么?

详解

在这个记录存储位置之间物品移动的系统中,storage_movementsstorage_items 之间存在has_many_and_belongs_to_many 关系,因为物品可以多次移动,并且可以一次移动多个物品。

这些项目是以前创建的,并由实际附加到项目并记录在应用程序上创建的车牌号来标识。

问题是我需要使用一个表单创建storage_movements,其中用户只输入正在移动的storage_item 的车牌号,但我想不出一种方法来轻松地做到这一点。

一段时间以来,我一直在撞墙,我能想到的唯一解决方案是在 storage_items 的新 storage_movements 表单上创建嵌套字段,并使用模型上的特定代码来创建,通过显式查询这些车牌号并操作这些操作的连接表条目来更新和删除这些storage_movements

这是处理问题的正确方法吗?这个解决方案的主要问题是我似乎无法在错误的特定车牌号上显示验证错误(我正在使用 simple_forms),因为我没有 storage_item 对象来添加错误。

下面是我当前使用的表单的代码片段。欢迎任何帮助:D

# views/storage_movements/_form.html.erb
<%= simple_form_for @storage_movement do |movement_form| %>
  #Other form inputs
  <%= movement_form.simple_fields_for :storage_items do |item_form| %>
    <%= item_form.input :plate, label: "Plate number" %>
  <% end %>
<% end %>

# models/storage_movement.rb
class StorageMovement < ActiveRecord::Base
  has_many_and_belongs_to_many :storage_items, inverse_of: :storage_movements, validate: true
  accepts_nested_attributes_for :storage_items, allow_destroy: true

  ... several callbacks and validations ...
end

# models/storage_item.rb
class StorageItem < ActiveRecord::Base
  has_many_and_belongs_to_many :storage_movements, inverse_of: :storage_items

  ... more callbacks and validations ...
end

控制器是默认生成的。

【问题讨论】:

  • 当你在游戏中放置stock_* 对象时,你有点迷失了我。这些与storage_* 对象有什么关系/我们是否也关心这些​​?我对实际问题是什么感到困惑。是您不知道如何处理表单中的车牌号以实际将其连接到正确的项目而不是使用其 id 还是其他什么?
  • 对不起,它们是一样的,我只是把它们命名错了。已经修复了文本。
  • 那么,您提供的代码看起来不错,应该可以工作。如果您添加了validates_associated 并正确配置了您的模型、验证和表单,您应该没问题。如果您尝试过这些或/并且遇到问题,请发布相关代码(控制器、模型、带有错误显示的表单等),我们可以从那里获取。
  • 按要求添加了它们。有了这个,当我尝试创建存储移动时,它会尝试创建新的存储项目,而不是使用基于板的现有存储项目,但由于存在验证而失败。此外,当我们尝试更新动作时,它会编辑项目的板块,而不是创建新的连接表记录......
  • 我认为默认生成的控制器不会为您的nested_resources 构建新记录。您提供的答案似乎很糟糕,将来可能会咬到您的底部(:P)。至少你可能想在继续之前检查 virtual_attributes。但是,问题可能出在您的控制器设置中。 simple_form_for 是否自动显示每个字段的错误(在您的视图代码中没有注意到任何与错误相关的内容,这就是我想知道的原因)?

标签: ruby-on-rails nested-forms jointable


【解决方案1】:

这是我的解决方案,它真的“感觉”错了,验证也没有像我想要的那样显示......但这是我能想到的......希望它对某人有所帮助。

我在模型上创建了create_from_platesupdate_from_plates 方法来处理创建和更新以及更新控制器的操作以使用它们。

注意:由于回调的需要,必须切换到 has_many through 关联。

# models/storage_movement.rb
class StorageMovement < ActiveRecord::Base
  has_many :movements_items, dependent: :destroy, inverse_of: :storage_movement
  has_many :storage_items, through: :movements_items, inverse_of: :allocations, validate: true
  accepts_nested_attributes_for :storage_items, allow_destroy: true

  validate :check_plates

  def StorageMovement::create_from_plates mov_attributes
    attributes = mov_attributes.to_h
    items_attributes = attributes.delete "items_attributes"

    unless items_attributes.nil?
      item_plates = items_attributes.collect {|k, h| h["plate"]}
      items = StorageItem.where, plate: item_plates
    end

    if not items_attributes.nil? and item_plates.length == items.count
      new_allocation = Allocation.new attributes
      movements_items.each {|i| new_allocation.items << i}
      return new_allocation
    else
      Allocation.new mov_attributes
    end
  end

  def update_from_plates mov_attributes
    attributes = mov_attributes.to_h
    items_attributes = attributes.delete "items_attributes"

    if items_attributes.nil?
      self.update mov_attributes
    else
      transaction do
        unless items_attributes.nil?
          items_attributes.each do |k, item_attributes|
            item = StorageItem.find_by_plate(item_attributes["plate"])
            if item.nil?
              self.errors.add :base, "The plate #{item_attributes["plate"]} was not found"
              raise ActiveRecord::Rollback
            elsif item_attributes["_destroy"] == "1" or item_attributes["_destroy"] == "true"
              self.movements_items.destroy item
            elsif not self.items.include? item
              self.movements_items << item
            end
          end
        end
        self.update attributes
      end
    end
  end


  def check_plates
    movements_items.each do |i|
      i.errors.add :plate, "Plate not found" if StorageItem.find_by_plate(i.plate).nil?
    end
  end

  ... other validations and callbacks ...
end

有了这个,创建工作就像我想要的那样,因为在发生错误的情况下,验证会将错误添加到特定的项目属性。但是更新没有,因为它必须将错误添加到运动的基础上,因为没有项目。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-05-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-24
    相关资源
    最近更新 更多