基于具有关联属性的表单(如条形码或板号)创建连接表条目的最佳方法是什么?
在此系统中,记录存储位置之间的项目移动,has_many_and_belongs_to_many
和storage_movements
之间存在storage_items
关系,因为项目可以多次移动,多个项目可以移动到一次。
这些物品以前是创建过的,并由物品附在物品上的板号识别,并记录在物品的创建上。
问题在于我需要创建一个storage_movements
形式,其中用户只输入正在移动的storage_item
的板号,但我无法想出一个方法来轻松实现这一点
我已经在这个墙上撞了一段时间了,我能想到的唯一解决方案是在storage_movements
的新storage_items
表单上创建嵌套字段,并使用特定代码通过显式查询这些板号并操纵这些操作的连接表条目来创建,更新和删除这些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
控制器是默认生成的控制器。
答案 0 :(得分:0)
这是我的解决方案,它确实“感觉”错了,验证也没有像我想要的那样显示......但这是我能想到的......希望它有助于某人。
我在模型上创建了create_from_plates
和update_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
有了这个,创建就可以按照我想要的方式工作,因为如果出现错误,验证会将错误添加到特定的item属性中。但更新并不是因为它必须将错误添加到运动的基础,因为没有项目。