我正在使用Devise构建一个用于身份验证的Rails应用程序,其默认数据库迁移设置了以下列:
## Database authenticatable
t.string :email, null: false, default: ""
t.string :encrypted_password, null: false, default: ""
同时设置null: false
和default: ""
的目的是什么?
我的理解是,null: false
有效地使值成为必需的:即,尝试在该列中保存具有NULL
值的记录将在数据库级别失败,而不依赖于对模型的任何验证。
但是default: ""
基本上通过在保存之前简单地将NULL
值转换为空字符串来撤消它。
我知道对于可选列,您希望拒绝NULL
值,以确保该列中的所有数据都属于同一类型。但是,在这种情况下,电子邮件和密码绝对不是用户身份验证模型上的可选属性。我确信模型中有验证以确保您无法创建具有空电子邮件地址的用户,但是为什么要首先在此处设置default: ""
?它是否有一些好处或防止一些我没有考虑过的边缘情况?
广义地说:
- 要使列成为必填列,必须对其设置
null: false
。无论是创建新表还是更新现有表,都是如此。 - 如果您要更新现有表,数据库引擎将尝试在每行中使用
NULL
填充该新列。在这种情况下,必须使用default: ""
覆盖此行为,否则它将与null: false
冲突,迁移将失败。
关于设计:
Devise 使用两个单独的模板来构建迁移:migration.rb
用于创建新表,migration_existing.rb
用于更新现有表(请参阅 GitHub 上的源代码)。两个模板都调用相同的migration_data
方法来生成有问题的行(即指定null: false, default: ""
的行),但如上所述,default: ""
仅在后一种情况下才真正相关(有关更多信息,请参阅 O. Jones 的回答)。
因此,对您的问题的简短回答,特别是在 Devise 迁移的情况下,是"因为生成器使用借用的代码并不总是适用,但仍然不会破坏任何东西。
UNIQUE
列的注意事项:
请注意,在大多数流行的 SQL 引擎中,唯一索引列仍然可以包含多行NULL
值,只要它们不是必需的(自然)。但是使新列既是必需的又唯一的(即null: false
、default: ""
和unique: true
)的效果是无法添加它:数据库引擎尝试在每行中用空字符串填充新列,这与unique
约束冲突并导致迁移失败。
(此机制失败的唯一情况是,如果表中只有一行 — 它为新列获取一个空白字符串值,该值自然会传递唯一性约束,因为它是唯一的记录。
因此,另一种看待它的方式可能是,这些选项是一种安全机制,可防止您运行不应运行的迁移(即,追溯性地将所需列添加到已填充的表中)。
插入类型有所不同。例如,假设您有一个new_table
表,如下所示:
CREATE TABLE IF NOT EXISTS `new_table` (
`col1` VARCHAR(10) NOT NULL,
`col2` VARCHAR(10) NOT NULL DEFAULT '',
`col3` VARCHAR(10) NULL DEFAULT '');
当您使用显式插入NULL
时,您将获得NULL
:
INSERT INTO new_table(col1,col2,col3) VALUES('a','b',NULL);
'a','b',NULL
相同的技巧col2
将导致错误:
INSERT INTO new_table(col1,col2,col3) VALUES('a',NULL,'c');
但是当你使用隐式插入NULL
时,你会得到默认值:
INSERT INTO new_table(col1,col2) VALUES('a','b');
'a','b',''
的意思是,设置默认值不会阻止NULL
对此列的断言,而是仅在未显式给出值时使用。
某些应用程序软件对 NULL 值进行清理,但在零长度文本字符串上不这样做。在Oracle中,它们是一回事,但在MySQL中不是。
更改表以添加列时,事情变得有趣。在这种情况下,默认值是必需的,因此 DBMS 可以填充新列。
我认为这是因为MySQL的"严格模式"不允许您在不提供默认值的情况下禁止空值。
来自 mysql 文档: https://dev.mysql.com/doc/refman/8.0/en/data-type-defaults.html
对于没有显式DEFAULT子句的NOT NULL列中的数据输入,如果INSERT或PLACE语句不包含该列的值,或者UPDATE语句将该列设置为NULL,则MySQL根据当时有效的SQL模式处理该列:如果启用了严格SQL模式,则事务表会发生错误,并且该语句将回滚。对于非事务表,会发生错误,但如果多行语句的第二行或后续行发生此错误,则会插入前面的行。如果未启用严格模式,MySQL会将列设置为列数据类型的隐式默认值。