问题:activerecord (rails3),将范围与包含链接起来

发布于 2024-09-12 17:56:25 字数 1559 浏览 3 评论 0原文

在 Rails3 中,链接两个作用域(ActiveRelations)时似乎存在问题,每个作用域都有不同的 include:

考虑这两个作用域,它们各自都可以正常工作:

第一个作用域:

scope :global_only, lambda { |user|
includes(:country)
.where("countries.area_id <> ?", user.area) }

Work.global_only(user) => (为了易读性,从 SQL 中删除字段列表)

SELECT * FROM "works" LEFT OUTER JOIN "countries" ON "countries"."id" = "works"."country_id" WHERE (countries.area_id <> 3)

现在是第二个范围:

scope :not_belonging_to, lambda { |user| 
includes(:participants)
.where("participants.user_id <> ? or participants.user_id is null", user) }

Work.not_belonging_to(user) => (为了易读性,从 SQL 中删除字段列表)

SELECT * FROM "works" LEFT OUTER JOIN "participants" ON "participants"."work_id" = "works"."id" WHERE (participants.user_id <> 6 or participants.user_id is null)

因此,这两个单独工作都可以正常工作。

现在,将它们链接在一起:

Work.global_only(user).not_belonging_to(user)

SQL:

SELECT (list of fields) FROM "works" LEFT OUTER JOIN "countries" ON "countries"."id" = "works"."country_id" WHERE (participants.user_id <> 6 or participants.user_id is null) AND (countries.area_id <> 3)

如您所见,来自第二个作用域的联接被完全忽略。因此,SQL 在“没有这样的列 'participants.user_id'”时失败。如果我以相反的顺序链接范围,则“参与者”连接将出现,而“国家”连接将丢失。似乎总是丢失第二个连接。

这看起来像 ActiveRecord 的错误吗,还是我做错了什么,或者这是一个“功能”:-)

(PS。是的,我知道,我可以创建一个连接两个表的范围,它将正确产生结果我想要。但我试图制作比可以以不同方式链接在一起的更小的范围,这应该是 activerecord 相对于直接 sql 的优势。)

In Rails3 there seems to be a problem when chaining two scopes (ActiveRelations) that each have a different include:

Consider these two scopes, both of which work fine on their own:

First scope:

scope :global_only, lambda { |user|
includes(:country)
.where("countries.area_id <> ?", user.area) }

Work.global_only(user) => (cut list of fields from SQL for legibility)

SELECT * FROM "works" LEFT OUTER JOIN "countries" ON "countries"."id" = "works"."country_id" WHERE (countries.area_id <> 3)

Now the second scope:

scope :not_belonging_to, lambda { |user| 
includes(:participants)
.where("participants.user_id <> ? or participants.user_id is null", user) }

Work.not_belonging_to(user) => (cut list of fields from SQL for legibility)

SELECT * FROM "works" LEFT OUTER JOIN "participants" ON "participants"."work_id" = "works"."id" WHERE (participants.user_id <> 6 or participants.user_id is null)

So both of those work properly individually.

Now, chain them together:

Work.global_only(user).not_belonging_to(user)

The SQL:

SELECT (list of fields) FROM "works" LEFT OUTER JOIN "countries" ON "countries"."id" = "works"."country_id" WHERE (participants.user_id <> 6 or participants.user_id is null) AND (countries.area_id <> 3)

As you can see, the join from the second scope is ignored altogether. The SQL therefore fails on 'no such column 'participants.user_id'. If I chain the scopes in the reverse order, then the 'participants' join will be present and the 'countries' join will be lost. It's always the second join that is lost, it seems.

Does this look like a bug with ActiveRecord, or am I doing something wrong, or is this a "feature" :-)

(PS. Yes, I know, I can create a scope that joins both tables and it will correctly yield the result I want. I have that already. But I was trying to make smaller scopes than can be chained together in different ways, which is supposed to be the advantage of activerecord over straight sql.)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

苯莒 2024-09-19 17:56:25

作为一般规则,使用 :includes 进行急切加载,使用 :joins 进行条件。在第二个作用域中,由于需要左连接,因此必须手动编写连接SQL。

也就是说,试试这个:

scope :global_only, lambda { |user|
  joins(:country).
  where(["countries.area_id != ?", user.area])
}

scope :not_belonging_to, lambda { |user|
  joins("left join participants on participants = #{user.id}").
  where("participants.id is null")
}

Work.global_only(user).not_belonging_to(user)

As a general rule, use :includes for eager-loading and :joins for conditions. In the second scope, the join SQL must be manually written because a left join is required.

That said, try this:

scope :global_only, lambda { |user|
  joins(:country).
  where(["countries.area_id != ?", user.area])
}

scope :not_belonging_to, lambda { |user|
  joins("left join participants on participants = #{user.id}").
  where("participants.id is null")
}

Work.global_only(user).not_belonging_to(user)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文