我想在模型创建/表单提交过程中加入一个检查现有关系对象的步骤。例如,假设我有一个Paper
模型has_and_belongs_to_many :authors
。在我的“创建论文”表单中,我想为authors_attributes
设置:name
字段,然后,在我的create
方法中,我想首先查看是否作者存在于“数据库”中;如果是,请将该作者添加到论文的authors
,否则,执行初始化新作者的正常authors_attributes
步骤。
基本上,我想做类似的事情:
# override authors_attributes
def authors_attributes(attrs)
attrs.map!{ |attr| Author.where(attr).first_or_initialize.attributes }
super(attrs)
end
但是由于多种原因这不起作用(它混淆了Mongoid对方法的定义,并且你不能在id
中包含_attributes
,除非它已经在模型中注册)。
我知道处理这些类型情况的首选方法是使用“表单对象”(例如,使用Virtus)。但是,我有点反对这种模式,因为它需要重复字段定义和验证(至少我理解它)。
有没有一种简单的方法来处理这种行为?我觉得这一定是一种常见的情况,所以我一定要错过一些东西......
答案 0 :(得分:2)
我过去解决这个问题的方法是允许从某种选择列表中选择现有记录(大型参考表的搜索对话框或较小参考表的选择框)。对话框或下拉列表中包含一种创建新引用的方法,而不是选择其中一个现有项目。
使用该方法,您可以检测记录是否已存在或是否需要创建。它避免了first_or_initialize
的需要,因为用户的意图应该从提交给控制器的内容中清楚。
当用户不想花时间在列表中找到他们想要的内容时,这种方法很难实现。如果发生验证错误,您可以为用户显示友好的内容,例如“您是否要选择[已存在的记录]?”这也可能有所帮助。
答案 1 :(得分:0)
如果我有模型Paper
:
class Paper
include Mongoid::Document
embeds_many :authors
accepts_nested_attributes_for :authors
field :title, type: String
end
Author
中嵌入的模型Paper
:
class Author
include Mongoid::Document
embedded_in :paper, inverse_of: :authors
field :name, type: String
end
我可以在控制台中执行此操作:
> paper = Paper.create(title: "My Paper")
> paper.authors_attributes = [ {name: "Raviolicode"} ]
> paper.authors #=> [#<Author _id: 531cd73331302ea603000000, name: "Raviolicode">]
> paper.authors_attributes = [ {id: paper.authors.first, name: "Lucia"}, {name: "Kardeiz"} ]
> paper.authors #=> [#<Author _id: 531cd73331302ea603000000, name: "Lucia">, #<Author _id: 531cd95931302ea603010000, name: "Kardeiz">]
如您所见,我可以在同一authors
哈希中更新并添加authors_attributes
。
有关详细信息,请参阅Mongoid nested_attributes article
答案 2 :(得分:0)
我遵循了question已接受答案的建议,并在reject_if
声明中实施了accepts_nested_attributes_for
警卫,如:
accepts_nested_attributes_for :authors, reject_if: :check_author
def check_author(attrs)
if existing = Author.where(label: attrs['label']).first
self.authors << existing
true
else
false
end
end
这似乎仍然是一个黑客,但它也适用于Mongoid ......