我有2个型号:
class PollAnswer < ActiveRecord::Base
belongs_to :poll, inverse_of: :poll_answers
# Validations
validates :answer, presence: true, uniqueness: { scope: :poll_id, case_sensitive: false }
validates :votes_number, presence: true
validates :poll, presence: true
end
class Poll < ActiveRecord::Base
# Associations
has_many :poll_answers, inverse_of: :poll, dependent: :destroy
# Attributes
accepts_nested_attributes_for :poll_answers, reject_if: :all_blank, allow_destroy: true
# Validations
validates :question, presence: true
validate :has_minimum_2_poll_answers
private
def has_minimum_2_poll_answers
remaining_poll_answers = poll_answers.reject(&:marked_for_destruction?)
if remaining_poll_answers.size < 2
errors.add :poll_answers, I18n.t(:minimum_2_poll_answers, scope: "activerecord.errors.models.polls")
end
end
end
和一个简单的测试:
let(:new_poll_answer) { build(:poll_answer) }
let(:new_poll_answer1) { build(:poll_answer) }
let(:new_poll) { create(:poll) }
it "validates the uniqueness of an answer scoped to poll_id" do
new_poll_answer.answer = "andre"
new_poll_answer.poll = new_poll
new_poll_answer1.answer = "andre"
new_poll_answer1.poll = new_poll
expect(new_poll_answer.valid?).to eq(false)
end
它失败了:
1) PollAnswer validations validates the uniqueness of an answer scoped to poll_id
Failure/Error: expect(new_poll_answer.valid?).to eq(false)
expected: false
got: true
(compared using ==)
# ./spec/models/poll_answer_spec.rb:22:in `block (3 levels) in <top (required)>'
# -e:1:in `<main>'
任何想法为什么?
更新:
在Marek Lipka评论之后,我可以看到这正是我的问题,因为这就是accepts_nested_attributes_for的工作原理。所以它不会验证唯一性。
我试过这个测试:
it "validates the uniqueness of an answer scoped to poll_id" do
new_poll_answer.answer = "andre"
new_poll_answer.poll = new_poll
new_poll_answer1.answer = "andre"
new_poll_answer1.poll = new_poll
new_poll.save
puts "#{new_poll.inspect}"
puts "#{new_poll_answer.inspect}"
puts "#{new_poll_answer1.inspect}"
expect(new_poll_answer1.valid?).to eq(false)
end
我得到了这个:
#<Poll id: 62, question: "Question", created_at: "2014-04-08 12:31:06", updated_at: "2014-04-08 12:31:06", active: true, published_at: "2014-04-08 11:31:06">
#<PollAnswer id: nil, answer: "andre", votes_number: 0, poll_id: 62, created_at: nil, updated_at: nil>
#<PollAnswer id: nil, answer: "andre", votes_number: 0, poll_id: 62, created_at: nil, updated_at: nil>
Failures:
1) PollAnswer validations validates the uniqueness of an answer scoped to poll_id
Failure/Error: expect(new_poll_answer1.valid?).to eq(false)
expected: false
got: true
(compared using ==)
如果您查看名为has_minimum_2_poll_answers的poll类的自定义验证,对我来说仍然很奇怪。
我怎样才能正确验证只有在没有poll_answers具有相同答案的情况下才能创建民意调查?
答案 0 :(得分:1)
您没有保存第一个new_poll_answer
,唯一性验证不会对未保存的记录起作用。你需要这样做:
new_poll_answer.save
在测试new_poll_answer1
之前的有效性。
答案 1 :(得分:0)
另请注意,除了先前已保存其中一条记录的要求之外,无法保证应用程序层中的唯一性验证。只能使用数据库层约束(即唯一索引)来保证唯一性验证。
摘自Rails文档:
将此验证方法与ActiveRecord :: Validations#save结合使用并不能保证不存在重复的记录插入,因为应用程序级别的唯一性检查本质上容易出现竞争条件。例如,假设两个用户尝试同时发布评论,并且评论的标题必须是唯一的。在数据库级别,这些用户执行的操作可以按以下方式进行交错: