我注意到有关外键和丢失关联的一些令人惊讶的行为。我有以下型号:
class Paper < ApplicationRecord
belongs_to :submission
has_one :author_info
end
class Submission < ApplicationRecord
has_one :paper
end
class AuthorInfo < ApplicationRecord
belongs_to :paper
end
在rspec测试中,我这样做:
let(:paper) { FactoryBot.create(:paper) }
和我的FactoryBot工厂这样做:
factory :paper do
< sets fields >
after(:create) do |paper|
paper.submission = FactoryBot.create(:submission)
paper.author_info = FactoryBot.create(:author_info)
end
end
[当我在控制台中检查这些对象时,可以执行paper.submission
和paper.author_info
以检索期望的模型。例如,paper.submission_id
是2,paper.submission.id
也是2。
问题1:
[我的理解是,当我调用paper.submission
时,Rails在ID为paper.submission_id
的提交表中查找所有行。所以我希望这样:
paper.submission_id = 1234
不要引起这个,因为我还没有保存到数据库:
paper.submission # nil
为什么这会导致协会迷路?
问题2:
说我刚刚创建了与问题1中相同的对象,但是没有对其进行修改(因此paper.id
从1开始)。现在,我这样做:
paper.id = 1234
我仍然可以执行paper.author_info
并取回原始的author_info
。鉴于问题1中发生的情况,这令人惊讶,因为paper.author_info.paper_id
仍为1!因此,我希望Rails可以查找ID为1的所有Paper
。在这种情况下,它看起来[[does似乎Rails正在使用DB查找关联并有效地忽略了未保存的外键的值,内存中的对象。
rails console
中发生了什么。[10] pry(main)> paper.submission_id = 1234
=> 1234
[11] pry(main)> paper.submission
Submission Load (0.6ms) SELECT "submissions".* FROM "submissions" WHERE "submissions"."id" = $1 LIMIT $2 [["id", 1234], ["LIMIT", 1]]
=> nil
一旦更改了关联的ID,Rails就会尝试查找该关联。这使您可以通过ID更改关联,而不必首先经历昂贵的加载对象。如果paper.submission
不变,则paper.submission.id
和paper.submission_id
将不再是同义词。这将变得非常混乱。
对问题2 ...paper.id = 1234
我仍然可以执行paper.author_info并找回原始author_info这是由于关联缓存。
如果您已经参考过
paper.author_info
,则可以使用only。然后将其缓存。或者,在您的情况下,因为FactoryBot将其分配为
如果重新加载paper.author_info = FactoryBot.create(:author_info)
。没有查询,它只是一个属性查询。paper
,请更改paper.id
并尝试paper.author_info
,您将获得nil
。关联将不会被缓存,它将尝试查找与Paper 1234关联的AuthorInfo。
# paper.reload will not work because the ID is wrong paper = Paper.find(original_paper_id) paper.id = 1234 paper.author_info # nil AuthorInfo Load (0.6ms) SELECT "author_infos".* FROM "author_infos" WHERE "author_infos"."paper_id" = $1 LIMIT $2 [["paper_id", 1234], ["LIMIT", 1]] => nil