Rails:为什么我会失去联系?

问题描述 投票:0回答:1

我注意到有关外键和丢失关联的一些令人惊讶的行为。我有以下型号:

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.submissionpaper.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何时在数据库中通过外键查找关联,而实际上是在未保存的内存对象中使用外键?
ruby-on-rails factory-bot
1个回答
0
投票
对于问题1,我们可以看到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.idpaper.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

© www.soinside.com 2019 - 2024. All rights reserved.