我有两个字段,名称和电子邮件,其组合应该是唯一的,不考虑案例。这是类和规范的代码。前3个测试通过,但不是第4个测试。
class Person < ActiveRecord::Base
validates :name, presence: true
validates :email, presence: true
validates_uniqueness_of :email, :scope => :name, :case_sensitive => false
validates_uniqueness_of :name, :scope => :email, :case_sensitive => false
end
describe Person do
context "with duplicate name and email" do
before do
@person1 = create(:person)
end
it "for case-sensitive match of both" do
expect(build(:person, {name: @person1.name, email: @person1.email})).to_not be_valid
end
it "for case-insensitive match of name" do
expect(build(:person, {name: @person1.name.swapcase, email: @person1.email})).to_not be_valid
end
it "for case-insensitive match of email" do
expect(build(:person, {name: @person1.name, email: @person1.email.swapcase})).to_not be_valid
end
it "for case-insensitive match of both" do
expect(build(:person, {name: @person1.name.swapcase, email: @person1.email.swapcase})).to_not be_valid
end
end
end
答案 0 :(得分:1)
我尝试使用以下方法找出您遇到的问题:
class Person < ActiveRecord::Base
validates :name, presence: true
validates :email, presence: true
validates_uniqueness_of :name, :case_sensitive => false, :scope => [ :email ]
validates_uniqueness_of :email, :case_sensitive => false, :scope => [ :name ]
end
和
require 'spec_helper'
describe Person do
context 'with duplicate name and email' do
before do
@person1 = Person.new(name: 'test@example.com', email: 'TEST@EXAMPLE.COM')
end
subject { @person1 }
it { should be_valid }
describe '(I) for case-sensitive match of both' do
before do
person = @person1.dup
person.save
end
it { should_not be_valid }
end
describe '(II) for case-insensitive match of name' do
before do
person = @person1.dup
person.name.swapcase!
person.save
end
it { should_not be_valid }
end
describe '(III) for case-insensitive match of email' do
before do
person = @person1.dup
person.email.swapcase!
person.save
end
it { should_not be_valid }
end
describe '(IV) for case-insensitive match of both' do
before do
person = @person1.dup
person.name.swapcase!
person.email.swapcase!
person.save
end
it { should_not be_valid }
end
end
end
case_sensitive的行为在某种程度上是奇怪的,如我的示例案例的日志中所示:
(0.4ms) SAVEPOINT active_record_1
Person Exists (1.2ms) SELECT 1 AS one FROM "people" WHERE (LOWER("people"."name") = LOWER('TEST@EXAMPLE.COM') AND "people"."email" = 'test@example.com') LIMIT 1
Person Exists (0.5ms) SELECT 1 AS one FROM "people" WHERE (LOWER("people"."email") = LOWER('test@example.com') AND "people"."name" = 'TEST@EXAMPLE.COM') LIMIT 1
SQL (3.7ms) INSERT INTO "people" ("created_at", "email", "name", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["created_at", Sat, 07 Sep 2013 14:21:05 UTC +00:00], ["email", "test@example.com"], ["name", "TEST@EXAMPLE.COM"], ["updated_at", Sat, 07 Sep 2013 14:21:05 UTC +00:00]]
(0.2ms) RELEASE SAVEPOINT active_record_1
Person Exists (0.3ms) SELECT 1 AS one FROM "people" WHERE (LOWER("people"."name") = LOWER('test@example.com') AND "people"."email" = 'TEST@EXAMPLE.COM') LIMIT 1
Person Exists (0.3ms) SELECT 1 AS one FROM "people" WHERE (LOWER("people"."email") = LOWER('TEST@EXAMPLE.COM') AND "people"."name" = 'test@example.com') LIMIT 1
(0.2ms) ROLLBACK
问题似乎是“LOWER”语句仅用于 电子邮件或名称,但不能同时用于两者。基本上,我希望你的代码能够正常工作。
但是,正如db-log中所见,并且在另一个问题(Rails 3. Validating email uniqueness and case sensitive fails)中也指出了,当性能成为问题时,确保约束中的不区分大小写的行为可能不是一个好主意;-)而是使用前置过滤器以小写字母保存电子邮件/名称。 因为这可能也不是最好的想法(因为你很可能不希望丢失名称上的案例信息),你可以使用另一个小写名称col来确保约束或相应地使用after_valition过滤器。 / p>
使用以下模型应该使测试套件变绿:
class Person < ActiveRecord::Base
validates :name, presence: true
validates :email, presence: true
before_validation :downcase_name_email
validates_uniqueness_of :name, :case_sensitive => false, :scope => [ :email ]
validates_uniqueness_of :email, :case_sensitive => false, :scope => [ :name ]
private
def downcase_name_email
self.email = self.email.downcase if self.email.present?
self.name = self.name.downcase if self.name.present?
end
end
最佳, 本。
P.S。:我将采用小写方法,确保迁移您的db-data:
Person.update_all('email = LOWER(email)')
Person.update_all('name = LOWER(name)')