如何在`accepts_nested_attributes_for`中使用`first_or_initialize`步骤 - Mongoid

时间:2014-02-28 17:56:46

标签: ruby-on-rails ruby mongoid

我想在模型创建/表单提交过程中加入一个检查现有关系对象的步骤。例如,假设我有一个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)。但是,我有点反对这种模式,因为它需要重复字段定义和验证(至少我理解它)。

有没有一种简单的方法来处理这种行为?我觉得这一定是一种常见的情况,所以我一定要错过一些东西......

3 个答案:

答案 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 ......

相关问题