编写一个没有 `find_by_sql` 的 Arel 内连接查询
Write an Arel inner join query without `find_by_sql`
在一个 Rails 5 项目中,我有一个查询方法用于当前使用 SQL 字符串的关联,我正在尝试将其重构为仅使用 Arel。我想知道是否可以在不使用 to_sql
和 find_by_sql
.
的情况下编写整个查询方法
查询是一个 INNER_JOIN 和一个连接 table。
这是旧的 SQL 字符串方法:
class User < AbstractModel
...
# Return an Array of Projects that this User is an admin for.
def projects_admin
@projects_admin ||= Project.find_by_sql(%(
SELECT projects.* FROM projects, user_groups_users
WHERE projects.admin_group_id = user_groups_users.user_group_id
AND user_groups_users.user_id = #{id}
))
end
这是我的 Arel 重写,得到相同的查询结果:
# Return an Array of Projects that this User is an admin for.
def projects_admin
projects = Project.arel_table
# for join tables with no model, need to create an Arel::Table object
# so we can use Arel methods on it, eg access columns
u_g_users = Arel::Table.new("user_groups_users")
arel = projects.project(Arel.star).join(u_g_users).
on(projects[:admin_group_id].eq(u_g_users[:user_group_id]).
and(u_g_users[:user_id].eq(id)))
# is there a way to write this without going to_sql and back?
Project.find_by_sql(arel.to_sql)
end
我想做的是更改我的新 projects_admin
方法的最后一行,以便它直接调用关系,而无需在 arel.to_sql
上使用 find_by_sql
。
如果我只是 return arel
,它不是我期望的 Project
数组。
但也许这是一种荒谬的愿望,必须这样说。许多例子表明情况就是如此。
我已经尝试了所有我能找到的例子,但我认为问题的根源是我误解了连接如何与我生成的对象 class 交互,Arel::SelectManager
.
这里是有问题的模型关联。 “user_groups_users”是“user_groups”和“users”之间的联接table - 这里只有“User”、“UserGroup”和“Project”模型。
class User < AbstractModel
has_many :projects_created, class_name: "Project"
has_and_belongs_to_many :user_groups,
class_name: "UserGroup",
join_table: "user_groups_users"
class UserGroup < AbstractModel
has_and_belongs_to_many :users
has_one :project
has_one :admin_project, class_name: "Project", foreign_key: "admin_group_id"
class Project < AbstractModel
belongs_to :admin_group, class_name: "UserGroup",
foreign_key: "admin_group_id"
belongs_to :user
belongs_to :user_group
我相信您忽略了一个更简单的解决方案,即添加间接关联并执行 LEFT JOIN:
class Project < AbstractModel
belongs_to :admin_group, class_name: "UserGroup",
foreign_key: "admin_group_id"
has_many :admin_group_users,
through: :admin_group,
source: :users # the name of the assocation on UserGroup
# ...
end
Project.joins(:admin_group_users)
.where(users: { id: id })
在一个 Rails 5 项目中,我有一个查询方法用于当前使用 SQL 字符串的关联,我正在尝试将其重构为仅使用 Arel。我想知道是否可以在不使用 to_sql
和 find_by_sql
.
查询是一个 INNER_JOIN 和一个连接 table。
这是旧的 SQL 字符串方法:
class User < AbstractModel
...
# Return an Array of Projects that this User is an admin for.
def projects_admin
@projects_admin ||= Project.find_by_sql(%(
SELECT projects.* FROM projects, user_groups_users
WHERE projects.admin_group_id = user_groups_users.user_group_id
AND user_groups_users.user_id = #{id}
))
end
这是我的 Arel 重写,得到相同的查询结果:
# Return an Array of Projects that this User is an admin for.
def projects_admin
projects = Project.arel_table
# for join tables with no model, need to create an Arel::Table object
# so we can use Arel methods on it, eg access columns
u_g_users = Arel::Table.new("user_groups_users")
arel = projects.project(Arel.star).join(u_g_users).
on(projects[:admin_group_id].eq(u_g_users[:user_group_id]).
and(u_g_users[:user_id].eq(id)))
# is there a way to write this without going to_sql and back?
Project.find_by_sql(arel.to_sql)
end
我想做的是更改我的新 projects_admin
方法的最后一行,以便它直接调用关系,而无需在 arel.to_sql
上使用 find_by_sql
。
如果我只是 return arel
,它不是我期望的 Project
数组。
但也许这是一种荒谬的愿望,必须这样说。许多例子表明情况就是如此。
我已经尝试了所有我能找到的例子,但我认为问题的根源是我误解了连接如何与我生成的对象 class 交互,Arel::SelectManager
.
这里是有问题的模型关联。 “user_groups_users”是“user_groups”和“users”之间的联接table - 这里只有“User”、“UserGroup”和“Project”模型。
class User < AbstractModel
has_many :projects_created, class_name: "Project"
has_and_belongs_to_many :user_groups,
class_name: "UserGroup",
join_table: "user_groups_users"
class UserGroup < AbstractModel
has_and_belongs_to_many :users
has_one :project
has_one :admin_project, class_name: "Project", foreign_key: "admin_group_id"
class Project < AbstractModel
belongs_to :admin_group, class_name: "UserGroup",
foreign_key: "admin_group_id"
belongs_to :user
belongs_to :user_group
我相信您忽略了一个更简单的解决方案,即添加间接关联并执行 LEFT JOIN:
class Project < AbstractModel
belongs_to :admin_group, class_name: "UserGroup",
foreign_key: "admin_group_id"
has_many :admin_group_users,
through: :admin_group,
source: :users # the name of the assocation on UserGroup
# ...
end
Project.joins(:admin_group_users)
.where(users: { id: id })