Rails 活动记录 where OR 子句与祖父母 属性
Rails active record where OR clause with grandparent property
我有一个 Rails Pundit 政策范围,我正在努力弄清楚。
如果我有这3个模型:
User - has many stories
Story - belongs to a user and has many chapters
Chapter - belongs to a story
我希望任何用户都能够查看任何 已发布的章节或用户编写的任何章节(甚至 non-published 个)。
显然,如果我是写故事的作者,我应该能够查看我自己的故事以及我的故事的所有章节,即使它们没有出版。不过,其他人只能查看我发表的故事和那些发表的故事中发表的章节。
我试过这个:
class ChapterPolicy < ApplicationPolicy
class Scope < Scope
def resolve
scope.where(published: true).or(scope.where("story.user.id = ?", @user.id))
end
end
end
据说我们需要使用 JOINS 我读过,所以我也试过这个:
scope.where(published: true).or(scope.joins(:story).where(stories: { user: @user }))
但我得到错误:
Relation passed to #or must be structurally compatible. Incompatible values: [:joins, :references]
知道如何编写查询来执行我想要的操作吗?
我想我终于明白了:
scope.joins(story: :user).where("(stories.published = ? AND chapters.published = ?) OR stories.user_id = ?", true, true, @user.id)
希望有一种更非 SQL 的声明方式,但是哦,好吧,除非有人知道非 sql 的方式,否则我猜这会起作用。
你看过 Arel 了吗?
你可以有这样的东西:
chapters = Arel::Table.new(:chapter)
stories = Arel::Table.new(:story)
rel = chapters.joins(stories)
.where(chapters[:published].eq(true)
.where(stories[:published].eq(true))
.or(stories[:user_id].eq(@user.id))
scope.where(rel)
你可以用 Arel 表达式来写这个:
Chapter.joins(story: :user)
.where(Chapter.arel_table[:published].eq(true))
.where(Story.arel_table[:published].eq(true))
.or(Chapter.joins(story: :user).where(Story.arel_table[:user_id].eq(user.id)))
该表达式生成此查询:
SELECT "chapters".* FROM "chapters" INNER JOIN "stories" ON "stories"."id" = "chapters"."story_id" INNER JOIN "users" ON "users"."id" = "stories"."user_id" WHERE ("chapters"."published" = 't' AND "stories"."published" = 't' OR "stories"."user_id" = 1)
我有一个 Rails Pundit 政策范围,我正在努力弄清楚。
如果我有这3个模型:
User - has many stories
Story - belongs to a user and has many chapters
Chapter - belongs to a story
我希望任何用户都能够查看任何 已发布的章节或用户编写的任何章节(甚至 non-published 个)。
显然,如果我是写故事的作者,我应该能够查看我自己的故事以及我的故事的所有章节,即使它们没有出版。不过,其他人只能查看我发表的故事和那些发表的故事中发表的章节。
我试过这个:
class ChapterPolicy < ApplicationPolicy
class Scope < Scope
def resolve
scope.where(published: true).or(scope.where("story.user.id = ?", @user.id))
end
end
end
据说我们需要使用 JOINS 我读过,所以我也试过这个:
scope.where(published: true).or(scope.joins(:story).where(stories: { user: @user }))
但我得到错误:
Relation passed to #or must be structurally compatible. Incompatible values: [:joins, :references]
知道如何编写查询来执行我想要的操作吗?
我想我终于明白了:
scope.joins(story: :user).where("(stories.published = ? AND chapters.published = ?) OR stories.user_id = ?", true, true, @user.id)
希望有一种更非 SQL 的声明方式,但是哦,好吧,除非有人知道非 sql 的方式,否则我猜这会起作用。
你看过 Arel 了吗?
你可以有这样的东西:
chapters = Arel::Table.new(:chapter)
stories = Arel::Table.new(:story)
rel = chapters.joins(stories)
.where(chapters[:published].eq(true)
.where(stories[:published].eq(true))
.or(stories[:user_id].eq(@user.id))
scope.where(rel)
你可以用 Arel 表达式来写这个:
Chapter.joins(story: :user)
.where(Chapter.arel_table[:published].eq(true))
.where(Story.arel_table[:published].eq(true))
.or(Chapter.joins(story: :user).where(Story.arel_table[:user_id].eq(user.id)))
该表达式生成此查询:
SELECT "chapters".* FROM "chapters" INNER JOIN "stories" ON "stories"."id" = "chapters"."story_id" INNER JOIN "users" ON "users"."id" = "stories"."user_id" WHERE ("chapters"."published" = 't' AND "stories"."published" = 't' OR "stories"."user_id" = 1)