如何确保关系在多对多的

时间:2018-01-25 23:24:59

标签: ruby-on-rails ruby-on-rails-4 rspec

我有一个充当多对多关系的模型。类名是RelatedDocument这是自我解释的,我基本上将它用于相互关联的Document类实例。

我遇到了验证问题,例如我在RelatedDocument类中有这个问题:

validates :document, presence: true, uniqueness: { scope: :related_document }
validates :related_document, presence: true

这有效我无法创建重复的document_id/related_document_id行。但是,如果我想从关系的另一端使这个唯一,并将验证更改为:

validates :document, presence: true, uniqueness: { scope: :related_document }
validates :related_document, presence: true, uniqueness: { scope: :document }

这在另一方面不起作用。当我注意到这一点时,我正在写rspec测试。如何编写验证或自定义验证方法,以防止保存相同的id组合,无论它们来自哪一方?

更新

评论部分的第一条评论说第一个唯一性验证将照顾双方,我说这不仅仅是因为我的rspec测试失败,这里是:

describe 'relation uniqueness' do
    let!(:base_doc) { create(:document) }
    let!(:another_doc) { create(:document) }
    let!(:related_document) { described_class.create(document: another_doc, related_document: base_doc) }

    it 'raises ActiveRecord::RecordInvalid, not allowing duplicate relation links' do
      expect { described_class.create!(document: another_doc, related_document: base_doc) }
        .to raise_error(ActiveRecord::RecordInvalid)
    end

    it 'raises ActiveRecord::RecordInvalid, not allowing duplicate relation links' do
      expect { described_class.create!(document: base_doc, related_document: another_doc) }
        .to raise_error(ActiveRecord::RecordInvalid)
    end
  end

第二次测试失败。

2 个答案:

答案 0 :(得分:2)

如果您想考虑已经提供的自定义验证的替代方法,您可以在每次添加新记录时创建互惠关系并使用现有验证。

例如,当我说“文档A与文档B相关”时,我还会插入一条记录,说明“文档B与文档A相关”。您可以保持您的验证简单,并且在以后希望在某些情况下需要互惠关系时可以更轻松地实现逻辑(也许只有在文档来自不同的情况下,这种关系才会得到回报作者)。

以下是您要实现的模型回调类型的未经测试的示例:

if params[:line_item_id].present?
  @line_item = LineItem.find(params[:line_item_id])
else
  @line_item = LineItem.new
end

答案 1 :(得分:1)

自定义验证器应该在这里工作

validate :unique_document_pair

def unique_document_pair
  if RelatedDocument.exists?(:document => [self.document,self.related_document], :related_document => [self.document, self.related_document])
      errors.add :base, "error"
  end
end