Rails验证三种模型关系

时间:2012-11-12 04:57:34

标签: ruby-on-rails activerecord associations

我正在研究三种模式关系,其中一方面我不确定如何处理。这是基本关系:

class Taxonomy
  has_many :terms
  # attribute: `inclusive`, default => false
end

class Term
  belongs_to :taxonomy
  has_and_belongs_to_many :photos
end

class Photo
  has_and_belongs_to_many :terms
end

这是非常简单的东西,除了一件事:

分类标准可以是“包含”或“独占”。独家意味着条款是互斥的,包容性意味着它们不是。

现在,我可以在客户端处理这个没有问题,但我不太确定如何在照片(或其他地方)上设置一个基本上说明的验证:“验证不超过一个术语来自独家分类与此记录相关联。“

奖金问题

另外,虽然它不是必不可少的,但我一直认为在分类法和照片之间设置某种类型的连接会很好,即,我想要一种简单的方法来查询已经过的所有照片。根据给定分类法中的术语进行分类。

我想我可以用Photo.where('term_id in ?', Taxonomy.find(1).terms.map(&:id))之类的东西来做这件事,但显然这不是很优雅。我非常肯定has_many:因为条款对照片很不满意所以无法工作,所以如果有人知道建立这种关系的更优雅的方式,我很乐意听到它。

谢谢!


更新,以进一步解释分类法的想法:

如果分类标准是exclusive,即。 taxonomy.inclusive = false,然后只能从给定照片附加的分类中选择一个术语。现在,Photo可能会应用多个分类。例如:

分类:颜色,包容性 术语:红色(id 1),蓝色(id 2),绿色(id 3)

分类:地区,独家 条款:北美(id 4),南美(id 5)

所以我们假设我们有一张有很多红色和蓝色的照片,所以它得到了这两个词,它可能在南美洲,所以它得到了那个词。但是,照片不能同时出现在北美和南美洲。

所以在这种情况下,如果我们打电话:photo.terms.map &:id,我们会得到[1,2,4]

在客户端,我基本上可以做(伪代码)

form_for photo
- Taxonomy.each do |tax|
  - if tax.inclusive?
    - tax.terms.each do |term|
      - check_box term
  - else
    - tax.terms.each do |term|
      - radio_button term

但在模型方面,我想说,照片可以包含term_id 1,2,3中的任何一个,但只能是/或4和5。

这有意义吗?

1 个答案:

答案 0 :(得分:2)

对于验证问题,您可以在Term字段上为taxonomy_id字段添加唯一性验证,该字段仅在分类法不包含时执行,即:

class Term
  validates :taxonomy_id, uniqueness: true, unless: Proc.new { |term| term.taxonomy.inclusive }
end

您也可以使用custom validation method

执行此操作
class Term
  validate :uniqueness_of_taxonomy_if_exclusive

  def uniqueness_of_taxonomy_if_exclusive
    if !taxonomy.inclusive && Term.find_by_taxonomy_id(taxonomy_id)
      errors.add(:taxonomy_id, "already exists for this exclusive taxonomy")
    end
  end
end

请注意,在这种情况下,这是无用的,因为第一种方法会给出相同的结果,但如果您要进行更复杂的验证,则可能会有用。

对于问题的第二部分,以下是在ActiveRecord中进行查询的方法,我们假设taxonomy是您想要照片的分类法:

Photo.includes(:terms => :taxonomy).where('taxonomies.id' => taxonomy.id)

在您发表评论后更新

我相信您的验证应该在表格中进行,该表格会在照片和条款之间建立链接。如果你有以下Rails约定,它实际上是表photos_terms。 要在其上添加验证,您必须使其成为头等公民,即:为此表添加活动记录模型和id字段,假设您将其命名为PhotoTermLink。 以下是您想要的验证代码:

class PhotoTermLink
  belongs_to :photo
  belongs_to :term

  validates :term_id, uniqueness: { scope: :photo_id }, unless: Proc.new { |link| link.term.taxonomy.inclusive }
end