Rails迁移无法创建NOT NULL约束

3

我有一个针对Rails 3.2插件的数据库迁移:

class CreateIssueChangeApprovements < ActiveRecord::Migration
  def change
    create_table :issue_change_approvements do |t|
      t.timestamps
      t.references :issue, :null => false
      t.references :user, :null => false
      t.string :field_type, :limit => 30
      t.string :old_value
      t.string :value
      t.integer :approved_by_id
      t.boolean :is_closed, :default => 0
      t.string :status, :default => '', :limit => 30
    end
    add_index :issue_change_approvements, :issue_id
    add_index :issue_change_approvements, :user_id
  end
end

当我对MySQL数据库运行它时,我发现issue_iduser_id字段没有NOT NULL约束,也没有外键。

有人可以告诉我正确的方法吗?

更新:

奇怪的是,当我进入MySQL Workbench并为我的新创建的表生成脚本时,我得到了这个:

CREATE TABLE `issue_change_approvements` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `created_at` datetime NOT NULL,
  `updated_at` datetime NOT NULL,
  `issue_id` int(11) NOT NULL,
  `user_id` int(11) NOT NULL,
  `field_type` varchar(30) DEFAULT NULL,
  `old_value` varchar(255) DEFAULT NULL,
  `value` varchar(255) DEFAULT NULL,
  `approved_by_id` int(11) DEFAULT NULL,
  `is_closed` tinyint(1) DEFAULT '0',
  `status` varchar(30) DEFAULT '',
  PRIMARY KEY (`id`),
  KEY `index_issue_change_approvements_on_issue_id` (`issue_id`),
  KEY `index_issue_change_approvements_on_user_id` (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

然而,当我运行像这样的语句时:
insert into issue_change_approvements(value) values('hello')

它成功执行并添加了一行具有许多NULL值。

更新2: 这是describe issue_change_approvements显示的内容:

+----------------+--------------+------+-----+---------+----------------+
| Field          | Type         | Null | Key | Default | Extra          |
+----------------+--------------+------+-----+---------+----------------+
| id             | int(11)      | NO   | PRI | NULL    | auto_increment |
| created_at     | datetime     | NO   |     | NULL    |                |
| updated_at     | datetime     | NO   |     | NULL    |                |
| issue_id       | int(11)      | NO   | MUL | NULL    |                |
| user_id        | int(11)      | NO   | MUL | NULL    |                |
| field_type     | varchar(30)  | YES  |     | NULL    |                |
| old_value      | varchar(255) | YES  |     | NULL    |                |
| value          | varchar(255) | YES  |     | NULL    |                |
| approved_by_id | int(11)      | YES  |     | NULL    |                |
| is_closed      | tinyint(1)   | YES  |     | 0       |                |
| status         | varchar(30)  | YES  |     |         |                |
+----------------+--------------+------+-----+---------+----------------+

请在上述迁移之后,尽可能地包含您正在更改的表格。 - onebree
@onebree,这里没有表格。这是一个“create_table”迁移。 - svz
当您在MySQL控制台中尝试describe issue_change_approvements;时,会得到什么结果? - A Fader Darkly
我猜你没有启用 STRICT_TRANS_TABLES sql_mode,否则在你的插入语句中就会收到错误提示。 通过运行 show variables like "sql%" 检查 sql_mode 变量。mysql 5.6.4 默认是 set sql_mode='NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES'; - dre-hh
1个回答

2
Rails不会默认创建任何约束。 null:false选项只是不允许此SQL字段的null值,但它不会添加约束。 在Rails中,通常不使用foreign_key约束,而是在模型关系上设置所需的选项,例如has_many:issues,dependent:destroy 。 我猜这种策略是为了更快的模式迁移,减少停机时间。
然而,有一种选择可以通过显式指定它们来使用数据库级别的约束。您可以在rails 4.2之前使用foreigner gem,在rails 4.2中默认包含了它们的dsl。
参见: 使用外键保持(引用)完整性 2.5 外键支持

所以所有这些检查都只存在于应用程序级别,我需要明确地添加它们才能将它们传播到数据库级别,对吗? - svz
是的,多少是这样。它们在应用程序级别也不会默认存在。您必须通过正确使用关联来在应用程序级别设置此行为。例如设置 dependent: :destroy。http://guides.rubyonrails.org/association_basics.html。 - dre-hh
是的,那就是我想表达的意思。谢谢你的回答! - svz
我的经验是设置null: false将会添加一个数据库级别的约束。这与应用程序级别的约束不同,必须作为验证添加到模型中。 - A Fader Darkly
@dre-hh:我看过许多Rails应用程序,缺乏“非空”约束已经引起了问题。不仅是关联,还有数据列和查询结果。当您无法信任数据时,就无法信任应用程序。然而,Rails的宽容性意味着这些约束很少使用,因此添加它们可能会使一些非常受欢迎的插件产生混淆(例如Shoulda和RailsAdmin)。在代码中使用非空列和所谓的“null对象”可以带来更好、更强大和更准确的应用程序。 - A Fader Darkly
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接