无法从Ruby控制台取消链接表中的记录

时间:2017-09-25 16:26:43

标签: ruby-on-rails

通过Lynda.com关于Rails 5的课程,我遇到了一个小故障。不知道问题是我的设置,转速的差异(我相信教练正在使用v5.0.0,而我正在使用v5.1.4),我做错了,或者rails控制台中有一个错误

本课程是第6章关联,第2节“一对一关联”。本课使用两个表:主题和页面。他们的模型是:

class Subject < ApplicationRecord
  has_one :page
  # and a bunch of scopes from an earlier lesson
end

class Page < ApplicationRecord
  belongs_to :subject
end

使用MySQL v14.14 Distrib 5.7.19 for osx10.12。

mysql> SHOW FIELDS FROM subjects;
+------------+-------------+------+-----+---------+----------------+
| Field      | Type        | Null | Key | Default | Extra          |
+------------+-------------+------+-----+---------+----------------+
| id         | bigint(20)  | NO   | PRI | NULL    | auto_increment |
| name       | varchar(50) | YES  |     | NULL    |                |
| position   | int(11)     | YES  |     | NULL    |                |
| visible    | tinyint(1)  | YES  |     | 0       |                |
| created_at | datetime    | NO   |     | NULL    |                |
| updated_at | datetime    | NO   |     | NULL    |                |
+------------+-------------+------+-----+---------+----------------+

mysql> SHOW FIELDS FROM pages;
+------------+--------------+------+-----+---------+----------------+
| Field      | Type         | Null | Key | Default | Extra          |
+------------+--------------+------+-----+---------+----------------+
| id         | bigint(20)   | NO   | PRI | NULL    | auto_increment |
| subject_id | int(11)      | YES  | MUL | NULL    |                |
| name       | varchar(50)  | YES  |     | NULL    |                |
| permalink  | varchar(100) | YES  | MUL | NULL    |                |
| position   | int(11)      | YES  |     | NULL    |                |
| visible    | tinyint(1)   | YES  |     | 0       |                |
| created_at | datetime     | NO   |     | NULL    |                |
| updated_at | datetime     | NO   |     | NULL    |                |
+------------+--------------+------+-----+---------+----------------+

这里要观察的字段是pages.subject_id。请注意,它可以被取消。

在rails控制台中,我在上一课中创建了一系列主题记录。在本课程中,我将在页面中创建一条记录,并将其与主题记录相关联。大!完美的工作。然后我尝试取消链接,就像教练那样,但是我没有做错。

在Rails命令行中,我从数据库中检索了几个对象:

iirb(main):022:0* subject2 = Subject.find_by_name("Next Subject")
  Subject Load (0.6ms)  SELECT  `subjects`.* FROM `subjects` WHERE `subjects`.`name` = 'Next Subject' LIMIT 1
=> #<Subject id: 2, name: "Next Subject", position: 2, visible: true, created_at: "2017-09-21 21:17:25", updated_at: "2017-09-21 21:51:39">
irb(main):023:0> page2 = Page.find_by_name("Next Page")
  Page Load (3.3ms)  SELECT  `pages`.* FROM `pages` WHERE `pages`.`name` = 'Next Page' LIMIT 1
=> #<Page id: 6, subject_id: nil, name: "Next Page", permalink: "next", position: 2, visible: false, created_at: "2017-09-24 19:00:52", updated_at: "2017-09-24 19:53:38">

请注意,pages.subject_id为零。记录没有关联。

从Rails命令行,page2.subject和subject2.page都返回nil,如预期的那样。我可以将它们与:

联系起来
irb(main):029:0> subject2.page = page2
   (2.7ms)  BEGIN
  SQL (9.2ms)  UPDATE `pages` SET `subject_id` = 2, `updated_at` = '2017-09-25 14:40:09' WHERE `pages`.`id` = 6
   (3.0ms)  COMMIT
=> #<Page id: 6, subject_id: 2, name: "Next Page", permalink: "next", position: 2, visible: false, created_at: "2017-09-24 19:00:52", updated_at: "2017-09-25 14:40:09">

现在page2.subject和subject2.page正常工作。

然后问题。教师说我可以用subject2.page = nil取消记录链接,但是:

irb(main):037:0* subject2.page = nil
   (0.3ms)  BEGIN
   (0.2ms)  ROLLBACK
ActiveRecord::RecordNotSaved: Failed to remove the existing associated page. The record failed to save after its foreign key was set to nil.
    from (irb):37

声称记录无法保存。它可能是pages.subject_id中的nil(或null)值吗?

page2对象显示subject_id已重置为其原始值,而不是nil。如果我尝试将此设置为nil然后将page2保存到数据库,则会失败。

但是如果我从数据库重新加载内存中的page2对象,手动将subject_id设置为nil,然后保存page2,它就可以了!至少页面数据库显示正确的内容。

但是,如果我检查page2.subject和subject2.page,Rails命令行显示它们仍然是链接的。我需要在内存中显示正确结果之前,从数据库中重新加载这些对象。

似乎subject2.page = nil(适用于教师)取消链接数据库和内存中的记录 - 至少它应该如何工作。手动取消链接在数据库中工作,但需要从数据库更新内存以获得完整功能。

那是怎么回事?

感谢一百万人的帮助。

PS:Mac OS-X v10.12.6(Sierra),Ruby v2.4.1p111,Rails v5.4.1(均通过Homebrew安装),使用VS Code v1.16.1作为编辑器,并通过命令行进行项目设置(显示与教师完全相同的文件和目录)

添加了信息:schema.rb的相关部分

ActiveRecord::Schema.define(version: 20170920222914) do

  create_table "pages", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
    t.integer "subject_id"
    t.string "name", limit: 50
    t.string "permalink", limit: 100
    t.integer "position"
    t.boolean "visible", default: false
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["permalink"], name: "index_pages_on_permalink"
    t.index ["subject_id"], name: "index_pages_on_subject_id"
  end

  create_table "subjects", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
    t.string "name", limit: 50
    t.integer "position"
    t.boolean "visible", default: false
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

end

2 个答案:

答案 0 :(得分:0)

听起来您的数据库中可能存在外键约束吗?您可以更新原始帖子并提供整个db/schema.rb文件吗?

如果您不希望在取消外键时删除Page记录,则应在dependent: :nullify关联上指定has_one。这也可能解决您的问题。

class Subject < ApplicationRecord
  has_one :page, dependent: :nullify
  # and a bunch of scopes from an earlier lesson
end

答案 1 :(得分:0)

更详细地向下移动评论。

在Rails 5.0及更高版本中,他们已经改变了默认的belongs_to关联的工作方式。从它所说的upgrade guides

  如果关联不存在,

belongs_to现在将默认触发验证错误。

     

可以使用optional:true来关闭每个关联。

在你的问题中,你显示你的页面表(顺便说一下,谢谢你的详细信息),它表明你的外键可以为空。但是当你去保存时,rails会自己进行默认验证并回滚你的保存,因为你正在使用rails 5.1.4。

我认为这是一个默认验证,因为它有助于强制执行外键约束。通常,您删除subject并级联删除page,或删除page但保留subject。但有时你会想要根据你的结构做你要求的事情。

我假设您的以下教程使用rails 4.2。