Rails - 具有单一值一次的自定义验证

时间:2013-03-20 22:22:37

标签: ruby-on-rails validation

所以我有这两个模型:

class Tag < ActiveRecord::Base
has_many :event_tags
attr_accessible :tag_id, :tag_type, :value
end

class EventTag < ActiveRecord::Base
belongs_to :tag
attr_accessible :tag_id, :event_id, :region
end

和这个标签表:

**tag_id**    **tag_type**      **value**
    1         "funLevel"        "Boring..."
    2         "funLevel"        "A Little"
    3         "funLevel"        "Hellz ya"

    4         "generic"         "Needs less clowns"
    5         "generic"         "Lazer Tag"
    ...

我想做的是编写一个自定义验证,它会检查:

  • 每个event_id都附加只有一个“funLevel”的 tag_type,但可以有多个“通用”标记

例如:

t1 = EventTag.new(:tag_id => 1, :event_id =>777, :region => 'US')
t1.save  # success

t2 = EventTag.new(:tag_id => 2, :event_id =>777, :region => 'US')
t2.save  # failure 
         # because (event_id: 777) already has a tag_type of 
         # "funLevel" associated with it

t3 = EventTag.new(:tag_id => 4, :event_id =>777, :region => 'US')
t3.save  # success, because as (tag_id:4) is not "funLevel" type

我想出了一个丑陋的解决方案:

def cannot_have_multiple_funLevel_tag
  list_of_tag_ids = EventTag.where("event_id = ?", event_id).pluck(:tag_id)
  if(Tag.where("tag_id in ?", list_of_tag_ids).pluck(:tag_type).include? "funLevel")
    errors.add(:tag_id, "Already has a Fun Level Tag!")
end

刚开始使用rails,是否有更好/更优雅/更便宜的方式?

1 个答案:

答案 0 :(得分:0)

您的数据结构化方式意味着内置的Rails验证可能不会成为您的一大堆帮助。 如果 funLevel类可以直接访问EventTag属性,您可以使用以下内容:

# event_tag.rb
validate :tag_type, uniqueness: { scope: :event_id },
    if: Proc.new { |tag| tag.tag_type == "funLevel" }

(不幸的是,从快速测试中,您似乎无法验证虚拟属性的唯一性。)

如果没有这个,你可能会使用自定义验证。您自定义验证的明显改进(假设您希望在EventTag上进行验证)将不会运行验证,除非EventTagfunLevel标记:

def cannot_have_multiple_funLevel_tag
  return unless self.tag.tag_type == "funLevel"
  ...
end