为什么我的has_many通过相关记录(有时)只读?

Mar*_*lin 5 ruby activerecord has-many-through ruby-on-rails-3

我有三个ActiveRecord模型:Partner,MembershipChannel(这是一个STI模型,继承自Channel)和ChannelMembership(我不负责命名这些模型......)

当我通过合作伙伴协会加载ChannelMembership时,我有时(!)最终得到一个只读记录.这是在Rails 3.0.9中.相同的代码在2.3.11中没有这样做.

> p = Partner.first
> p.channel_memberships.map(&:readonly?)
# => [false, false, false, false, false, false]
> p.reload.channel_memberships.limit(1).first.readonly?
# => false
> p.reload.channel_memberships.first.readonly?
# => true
Run Code Online (Sandbox Code Playgroud)

调用关联readonly?时为什么是真的first,而不是来自关系limit

我知道readonly如果我在查找记录时使用SQL片段会触发,但这不是这种情况.通过关联只是一个简单的has_many.唯一复杂的问题是它加入了STI模型.更重要的是,从最后两个示例中查看生成的SQL,它们是完全相同的!

我可以通过指定:readonly => false关联来获得我想要的行为,但我想了解发生了什么.

Channel,MembershipChannel或ChannelMembership上没有默认范围.以下是合作伙伴的关联声明:

class Partner
  has_many :membership_channels
  has_many :channel_memberships, :through => :membership_channels
end
Run Code Online (Sandbox Code Playgroud)

这是我日志中生成的SQL:

  Partner Load (0.4ms)  SELECT "partners".* FROM "partners" LIMIT 1
  ChannelMembership Load (0.7ms)  SELECT "channel_memberships".* FROM "channel_memberships" INNER JOIN "channels" ON "channel_memberships".channel_id = "channels".id WHERE (("channels".partner_id = 2) AND (("channels"."type" = 'MembershipChannel')))
  Partner Load (0.5ms)  SELECT "partners".* FROM "partners" WHERE "partners"."id" = 2 LIMIT 1
  ChannelMembership Load (1.0ms)  SELECT "channel_memberships".* FROM "channel_memberships" INNER JOIN "channels" ON "channel_memberships".channel_id = "channels".id WHERE (("channels".partner_id = 2) AND (("channels"."type" = 'MembershipChannel'))) LIMIT 1
  Partner Load (0.4ms)  SELECT "partners".* FROM "partners" WHERE "partners"."id" = 2 LIMIT 1
  ChannelMembership Load (0.6ms)  SELECT "channel_memberships".* FROM "channel_memberships" INNER JOIN "channels" ON "channel_memberships".channel_id = "channels".id WHERE (("channels".partner_id = 2) AND (("channels"."type" = 'MembershipChannel'))) LIMIT 1
Run Code Online (Sandbox Code Playgroud)

Kri*_* PD 2

我能够通过基本的 has_many :through 关联重现您的问题,并且也了解导致该问题的原因。

据我所知,只有在原始对象上调用 reload 方法时才会发生这种情况。我不确定这是否是因为重新加载专门做了什么,或者可能是因为某些属性标志被重置?

我的第二个理论是,这与以下事实有关:

p.reload.channel_memberships.limit(1)
Run Code Online (Sandbox Code Playgroud)

返回一个 ActiveRecord::Relation,您可以通过它获得第一个 ChannelMembership,并且

p.reload.channel_memberships.first
Run Code Online (Sandbox Code Playgroud)

直接从关联加载它。也许重新加载重置某些缓存项目(我不知道 AR 来源)的某种组合将关联标记为只读。当您对其应用 limit(1) 范围时,它可能会在新关系中重置它们,并按您的预期工作。

我会进一步研究 ActiveRecord::Persistence / Associations 以获得完整的答案。