【发布时间】:2014-09-15 07:13:46
【问题描述】:
如果同一个表有多个连接,ActiveRecord 会设置别名表名。我陷入了这些联接包含范围(使用“合并”)的情况。
我有一个多对多的关系:
模型表名:
users第二个模型表名:
posts加入表名:
access_levels
一个帖子通过 access_levels 拥有许多用户,反之亦然。
User 模型和 Post 模型共享相同的关系:
has_many :access_levels, -> { merge(AccessLevel.valid) }
AccessLevel 模型内部的范围如下所示:
# v1
scope :valid, -> {
where("(valid_from IS NULL OR valid_from < :now) AND (valid_until IS NULL OR valid_until > :now)", :now => Time.zone.now)
}
# v2
# scope :valid, -> {
# where("(#{table_name}.valid_from IS NULL OR #{table_name}.valid_from < :now) AND (#{table_name}.valid_until IS NULL OR #{table_name}.valid_until > :now)", :now => Time.zone.now)
# }
我想这样称呼某事:
Post.joins(:access_levels).joins(:users).where (...)
ActiveRecord 为第二个连接创建一个别名('access_levels_users')。我想在 AccessLevel 模型的“有效”范围内引用这个表名。
V1 显然会产生PG::AmbiguousColumn-Error。
V2 导致两个条件都以access_levels. 为前缀,这在语义上是错误的。
这就是我生成查询的方式:(简化)
# inside of a policy
scope = Post.
joins(:access_levels).
where("access_levels.level" => 1, "access_levels.user_id" => current_user.id)
# inside of my controller
scope.joins(:users).select([
Post.arel_table[Arel.star],
"hstore(array_agg(users.id::text), array_agg(users.email::text)) user_names"
]).distinct.group("posts.id")
生成的查询如下所示(使用上面的 valid 范围 v2):
SELECT "posts".*, hstore(array_agg(users.id::text), array_agg(users.email::text)) user_names
FROM "posts"
INNER JOIN "access_levels" ON "access_levels"."post_id" = "posts"."id" AND (("access_levels"."valid_from" IS NULL OR "access_levels"."valid_from" < '2014-07-24 05:38:09.274104') AND ("access_levels"."valid_until" IS NULL OR "access_levels"."valid_until" > '2014-07-24 05:38:09.274132'))
INNER JOIN "users" ON "users"."id" = "access_levels"."user_id"
INNER JOIN "access_levels" "access_levels_posts" ON "access_levels_posts"."post_id" = "posts"."id" AND (("access_levels"."valid_from" IS NULL OR "access_levels"."valid_from" < '2014-07-24 05:38:09.274675') AND ("access_levels"."valid_until" IS NULL OR "access_levels"."valid_until" > '2014-07-24 05:38:09.274688'))
WHERE "posts"."deleted_at" IS NULL AND "access_levels"."level" = 4 AND "access_levels"."user_id" = 1 GROUP BY posts.id
ActiveRecord 为 access_levels 表的第二个连接设置一个适当的别名“access_levels_posts”。
问题是合并的valid-scope 为列添加了“access_levels”而不是“access_levels_posts”的前缀。我还尝试使用 arel 来生成范围:
# v3
scope :valid, -> {
where arel_table[:valid_from].eq(nil).or(arel_table[:valid_from].lt(Time.zone.now)).and(
arel_table[:valid_until].eq(nil).or(arel_table[:valid_until].gt(Time.zone.now))
)
}
结果查询保持不变。
【问题讨论】:
-
您的问题有点令人困惑,但我想我知道您想做什么。将
valid范围更改为joins(:user).where("(valid_from IS NULL OR valid_from < :now) AND (valid_until IS NULL OR valid_until > :now)", now: Time.zone.now).where(users: { active: true, or: something })
标签: sql ruby-on-rails-4 rails-activerecord arel