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

时间:2011-08-19 09:44:27

标签: ruby ruby-on-rails-3 activerecord has-many-through

我有三个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

为什么readonly?在关联时调用first时为真,而对limit的关系无效?

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

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

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

class Partner
  has_many :membership_channels
  has_many :channel_memberships, :through => :membership_channels
end

以下是我日志中生成的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

1 个答案:

答案 0 :(得分:2)

我能够通过一个基本的has_many:通过关联来重现你的问题,并且也是因为它导致了什么。

据我所知,只有在原始对象上调用reload方法时才会发生这种情况。我不确定这是因为任何重载正在做的事情,或者是因为某些属性标志正在重置?

我的第二个理论是它与

这个事实有关
p.reload.channel_memberships.limit(1)

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

p.reload.channel_memberships.first

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

我会更多地了解ActiveRecord :: Persistence / Associations以获得完整答案。