假设Rails 3与MySQL DB具有不区分大小写的排序规则
来龙去脉是什么:
Rails允许您使用“唯一性”验证器来验证模型的属性。但是根据Rails文档,默认比较是CASE SENSITIVE。
这意味着在验证时它会像下面这样执行SQL:
SELECT 1 FROM `Users` WHERE (`Users`.`email` = BINARY '[email protected]') LIMIT 1
对于拥有带有CI排序规则的数据库的我来说,这是完全错误的。即使“用户”表中已经存在另一个具有“[email protected]”的用户,它也会认为“[email protected]”是有效的。换句话说,这意味着,如果应用程序的用户尝试使用电子邮件“[email protected]”创建一个新用户,那么对于Rails来说,该用户名将完全有效(默认情况下),并且INSERT将被发送到db。如果您没有碰巧在电子邮件上拥有唯一索引,那么您将很忙-将插入行而不会出现问题。如果碰巧具有唯一索引,则将引发异常。
好的。 Rails说:由于您的数据库具有不区分大小写的排序规则,因此请执行不区分大小写的唯一性验证。
这是怎么做的?它告诉您可以通过在特定属性验证器上设置“:case_sensitive => false”来覆盖默认的唯一性比较灵敏度。在验证时,它将创建以下SQL:
SELECT 1 FROM `Users` WHERE (LOWER(`Users`.`email`) = LOWER('[email protected]') LIMIT 1
它是数据库表上的灾难,您设计为在电子邮件字段上具有唯一索引的用户(因为它不使用索引)对表进行了全表扫描。
现在,我看到SQL中的
LOWER
函数是由UniquenessValidator
的ActiveRecord
(文件uniqueness.rb
,模块ActiveRecord
,模块Validations
类UniquenessValidator
)插入的。这是执行此操作的代码段:if value.nil? || (options[:case_sensitive] || !column.text?)
sql = "#{sql_attribute} #{operator}"
else
sql = "LOWER(#{sql_attribute}) = LOWER(?)"
end
所以问题去了Rails/ActiveRecord而不是MySQL Adapter。
问题:是否有办法告诉Rails将关于唯一性验证区分大小写的要求传递给MySQL适配器,而不是“聪明”地更改查询?或者
进行澄清的问题:是否存在另一种对属性实现唯一性验证的方法(请小心,我只是讲电子邮件,例如电子邮件),并且区分大小写查询的生成将在相应列上使用简单的唯一索引?
这两个问题是等效的。我希望现在,我能使自己更加清晰,以获得更准确的答案。
最佳答案
验证唯一性,不考虑大小写
如果您要坚持以大写或小写形式存储电子邮件,则无论大小写如何,都可以使用以下代码来强制唯一性:
validates_uniqueness_of :email, case_sensitive: false
(另请参阅以下问题:
Rails "validates_uniqueness_of" Case Sensitivity)
完全消除大小写问题
与其进行不区分大小写的匹配,不如在验证之前(并因此)对电子邮件进行小写:
before_validation {self.email = email.downcase}
由于大小写与电子邮件无关,这将简化您的所有操作,并避免将来可能进行的比较或数据库搜索
关于mysql - 不区分大小写时,如何指示Rails在唯一性验证上生成正确的SQL,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/7713752/