【问题标题】:rails validates uniqueness is failing oddlyrails 验证唯一性奇怪地失败了
【发布时间】:2014-04-08 09:20:22
【问题描述】:

我有 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 发表评论后,我可以看到这正是我的问题,因为这就是 accept_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_answers 时才应创建投票?

【问题讨论】:

  • 我怀疑如果你真的尝试保存(而不是调用 valid)保存会失败
  • 但我的问题是,如果我必须保存一个包含许多 poll_answers 的投票,它将允许 poll_answers 具有相同的答案......这是我真正的问题
  • 这真的发生了吗?父对象和关联对象的保存被包装在一个事务中,所以如果任何一个保存失败,都将被回滚。
  • Frederick 你自己试试,复制我的代码并运行它!我同意你的最后一句话!

标签: ruby-on-rails validation ruby-on-rails-4 validates-uniqueness-of


【解决方案1】:

您没有保存您的第一个 new_poll_answer,唯一性验证不适用于未保存的记录。你需要做的:

new_poll_answer.save

在测试new_poll_answer1 的有效性之前。

【讨论】:

    【解决方案2】:

    还请注意,除了要求之前已保存记录之一之外,无法保证应用层中的唯一性验证。唯一性验证只能使用数据库层约束(即唯一索引)来保证。

    Rails 文档节选:

    并发性和完整性

    将此验证方法与 ActiveRecord::Validations#save 结合使用并不能保证不存在重复记录插入,因为应用程序级别的唯一性检查天生就容易出现竞争条件。例如,假设两个用户尝试同时发表评论,并且评论的标题必须是唯一的。在数据库级别,这些用户执行的操作可以按以下方式交错:

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-05
      • 1970-01-01
      • 1970-01-01
      • 2012-12-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多