如何在 has_many, through: 关联中验证关联记录的存在?

3

有一些模型具有has_many through关联:

class Event < ActiveRecord::Base
  has_many :event_categories
  has_many :categories, through: :event_categories

  validates :categories, presence: true
end

class EventCategory < ActiveRecord::Base
  belongs_to :event
  belongs_to :category

  validates_presence_of :event, :category
end

class Category < ActiveRecord::Base
   has_many :event_categories
   has_many :events, through: :event_categories
end

问题在于赋值event.categories = []会立即从event_categories中删除行。因此,先前的关联关系被不可逆地破坏,事件变得无效。
如何在has_many, through:的情况下验证记录是否存在?
更新:请在回答之前仔细阅读粗体标记的句子。 Rails 4.2.1
3个回答

2
你需要创建一个自定义验证,如下所示:

您需要创建自定义的验证,如下:

validate :has_categories

def has_categories
  unless categories.size > 0
    errors.add(:base, "There are no categories")
  end
end

这展示了一个通用的思路,你可以根据自己的需求进行调整。

更新

这篇文章又被提起了,我找到了一种填补空白的方法。

验证可以按照上面的方式保留。我只需要添加一个直接分配一个空类别集的情况。那么,我应该怎么做呢?

想法很简单:重写setter方法,不接受空数组:

def categories=(value)
  if value.empty?
    puts "Categories cannot be blank"
  else
    super(value)
  end
end

这将适用于每个任务,除了当分配空集时。那么,什么也不会发生。不会记录任何错误,也不会执行任何操作。

如果您想添加错误消息,您需要自己创造。添加一个属性到类中,在铃响时填充该属性。

简而言之,这个模型对我有效:

class Event < ActiveRecord::Base
  has_many :event_categories
  has_many :categories, through: :event_categories

  attr_accessor :categories_validator # The bell

  validates :categories, presence: true
  validate  :check_for_categories_validator # Has the bell rung?

  def categories=(value)
    if value.empty?
      self.categories_validator = true # Ring that bell!!!
    else
      super(value) # No bell, just do what you have to do
    end
  end

  private

  def check_for_categories_validator
    self.errors.add(:categories, "can't be blank") if self.categories_validator == true
  end
end

添加了这个最后的验证之后,如果您执行以下操作,实例将无效:
event.categories = []

尽管如此,没有任何操作会被执行(更新被跳过)。


当将 event.categories = [] 赋值时,它不会阻止立即从 event_categories 表中删除。 - y.bregey
如果您尝试使用 event_categories.size 会怎样呢? - Ruby Racer
立即从 event_categories 中删除相同的内容。 - y.bregey
你好,好久不见了。我已经更新了我的回答以涵盖更多情况。 - Ruby Racer

-2

使用validates_associated,官方文档在这里


从文档中 - “验证关联的对象或对象是否全部有效”和“如果尚未分配关联,则此验证不会失败。如果要确保关联存在且保证有效,还需要使用validates_presence_of。”但是即使是validates_presence_of:categoriesvalidates_associated:categories的组合也无法防止在分配event.categories = []时立即从event_categories表中删除。 - y.bregey

-3
如果您正在使用 RSpec 作为测试框架,请看一下 Shoulda Matcher。这里是一个例子:
describe Event do
  it { should have_many(:categories).through(:event_categories) }
end

我不是在问如何测试它,而是如何验证它。 - y.bregey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接