通过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
答案 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。