我如何给出别名,例如
includes()
?
以下给出:
这里有一些例子:
第一个案例(更多性传播感染协会)
Project.all.includes(:students, :teachers).order('teachers_projects.name ASC') # order on teachers
Project.all.includes(:students, :teachers).order('users.name ASC') # order on students
Rails 自动在 SQL 中为
teachers_projects
使用别名 :teachers
。我如何覆盖它,以便我可以在 SQL 中使用别名 teachers
而不是 teachers_projects
? :students
获取别名 users
。
这个例子失败了:
Project.all.includes(:students, :teachers).order('teachers.name ASC')
Project.all.includes(:students, :teachers).order('students.name ASC')
Project.all.includes(:students, :teachers).order('students_projects.name ASC')
第二种情况(一个性传播感染协会)
如果我在方法
:students
中仅使用:teachers
(不带includes()
),Rails使用STI基类名称users
(不附加_projects
)的名称别名作为:students
:
Project.all.includes(:students).order('users.name ASC') # order on students
这个例子失败了:
Project.all.includes(:students).order('students.name ASC')
Project.all.includes(:students).order('students_projects.name ASC')
问题
可能存在类似:
Project.all.includes(:students).alias(students: :my_alias)
RAILS 别名跟踪器
测试应用程序
Arel 实际上有一个别名方法。
student_alias = Student.arel_table.alias(:my_student_table_alias)
警告:这将要求您使用更多手工制作的 Arel 并手动进行连接。如果您不习惯 Arel 中的连接,它们可能会变得有点复杂。
student_alias_join = student_alias.create_on(
Project.arel_table[:primary_key].eq(student_alias[:project_id])
)
Project.joins(
Project.arel_table.create_join(
student_alias, student_alias_join, Arel::Nodes::OuterJoin
)
).order(...)
类似这样的事情应该可以做到。 当然,将其放入某个以
:my_student_table_alias
作为参数的类方法中会使其更加整洁和可重用,因为这在控制器中看起来有点混乱。
我将采取另一种方法来解决这个问题:我不会尝试使用
.alias
方法来控制查询上的别名,而是让 Rails / Arel 处理这个问题,然后只查看正确的表名(别名或不)只要在范围内需要它就向上。
将此辅助方法添加到您的模型中,您可以从作用域调用该方法,以了解该作用域是否正在具有表名别名(同一个表上的多个联接)的
JOIN
中使用,或者是否另一方面,作用域没有表名的别名。
def self.current_table_name
current_table = current_scope.arel.source.left
case current_table
when Arel::Table
current_table.name
when Arel::Nodes::TableAlias
current_table.right
else
fail
end
end
current_scope
作为基础对象来查找 arel 表。我在该对象上调用 source
来获取 Arel::SelectManager
,这反过来将为您提供 #left
上的当前表格。有两个选项:要么有一个 Arel::Table
(没有别名,表名位于 #name
上),要么有一个 Arel::Nodes::TableAlias
,其别名位于 #right
上。
现在您只需在您的
order
语句中使用它(未经测试):
Project.all.includes(:students, :teachers).order("#{current_table_name}.name ASC")
Project.all.includes(:students, :teachers).order("#{current_table_name}.name ASC")
Project.all.includes(:students, :teachers).order("#{current_table_name}.name ASC")
相关问题:
我的用例是对数据库进行一些一致性检查,其中我将每个模型与我发现的每个关联连接起来并执行查询。
也许可以用 @neongrau 的解决方案解决一些问题,但通过关联会变得更加复杂。
@dgilperez 答案仅适用于范围,但我不知道如何在我的用例中应用它。
因为这不是一个经常运行的性能关键的事情,所以我选择了一个更基本的解决方案 -
#to_sql
上的正则表达式来查看最后一个连接是如何生成的。 /尽管我总是对正则表达式的实际速度感到惊讶/
last_alias = last_table_alias_from_sql(model.joins(association.name).select(:id).to_sql)
found = model.joins(association.name).where("#{last_alias}.field = #{model.arel_table.name}.field")
正则表达式查找器是这样的:
def last_table_alias_from_sql(sql)
matcher = /.*JOIN [`'"]([\S]+)[`'"] (?:[`'"]([\S]+)[`'"] )?ON/i.match(sql)
matcher[2] || matcher[1]
end
association
是我从Model.reflect_on_all_associations
得到的东西,但如果你可以只使用任何Symbol
,例如:vehicles
决定将其作为针对奇怪用例的简单技巧。对于引号的内容,我尝试使其与数据库无关,但到目前为止仅在 MySQL 上进行了测试。您可能需要更改数据库的 RE。如果我记得我会在尝试其他答案时更新答案。