Rails自定义验证检查重复项

时间:2013-11-27 19:45:46

标签: ruby-on-rails ruby-on-rails-3 validation activerecord

表格包含以下字段:badge_id,output_id,fulfill,removed,updated_at。对于每个badge_id,不能有两个具有相同output_id的有效记录。但这并不意味着(badge_id,output_id)是一种独特的组合。删除列表示当前行已被删除。基本上删除或更新操作会触发在表中插入具有最新更改的新行。例如,我们有一个这样的记录:

badge_id| output_id| removed| timely | updated_at
1       | 1        | N      | Y      | 2013-11-26

要删除该记录,我们实际上插入了另一行,现在它读起来像

badge_id| output_id| removed| timely | updated_at
1       | 1        | N      | Y      | 2013-11-26
1       | 1        | Y      | Y      | 2013-11-27

由于(badge_id:1,output_id:1)的最新记录已删除列集,因此表示已删除该组合。但我不能有两行相同(badge_id:1,output_id:1),两者都删除为“N”,如:

badge_id| output_id| removed| timely | updated_at
1       | 1        | N      | N      | 2013-11-26
1       | 1        | N      | Y      | 2013-11-27

所以每次为某个badge_id添加一个新的output_id时,我都要检查是否有重复。但通常验证来自ActiveModel的(badge_id,output_id)的唯一性在这里不起作用。如何为此编写干净的自定义验证?感谢。

更新

我想我可能错过了一些关键点。可以添加一个记录然后删除,然后重复添加。因此(badge_id,output_id,removed)的组合也不是唯一的。添加新记录时,我们需要检查(badge_id,output_id),最新记录是否已将设置删除为“Y”。

因此可能的答案如

validate_uniqueness_of :badge_id, scope: [:output_id], 
conditions: -> { where(removed: "N") }

在条件where子句中,它应该有updated_at desc的顺序,并且第一个已删除:'N'。我如何将这种条件纳入这一行代码?或者有更好的方法来做到这一点?

3 个答案:

答案 0 :(得分:1)

您可以执行以下操作:

validates :unique_badge_and_output_ids

然后unique_badge_and_output_ids可能是:

def unique_badge_and_output_ids
  unless Object.find_by_badge_id_and_output_id_and_removed(self.badge_id, self.output_id, self.removed).blank?
    self.errors.add "record already exists" # obviously a better error here would be ideal
  end
end

答案 1 :(得分:0)

您可以在validates_uniqueness_of上指定SQL条件:

http://api.rubyonrails.org/classes/ActiveRecord/Validations/ClassMethods.html#method-i-validates_uniqueness_of

  

也可以将唯一性约束限制为一组   记录匹配某些条件。在此示例存档文章中   在验证的唯一性时没有考虑到   title属性:

class Article < ActiveRecord::Base
  validates_uniqueness_of :title, conditions: -> { where.not(status: 'archived') } 
end

所以在你的例子中:

class YourModel < AR::Base
  validate_uniqueness_of :badge_id, scope: [:output_id], 
    conditions: -> { where(removed: "N") }
end

答案 2 :(得分:0)

希望我能正确理解您的用例。

尝试验证removed的唯一性,并将其范围限定在badge_idoutput_id列,但仅限于删除的字段为N时:

class Model < ActiveRecord::Base
  validates_uniqueness_of :removed, 
    scope: [:badge_id, :output_id], 
    conditions: -> { where.not(removed: 'Y') }
end

它有可能发挥作用。