Rails 4 - 如何在活动记录查询中为包含()和连接()提供别名

Posted

技术标签:

【中文标题】Rails 4 - 如何在活动记录查询中为包含()和连接()提供别名【英文标题】:Rails 4 - how to give alias names to includes() and joins() in active record querying 【发布时间】:2015-04-20 03:55:51 【问题描述】:

我怎样才能给例如一个别名? includes()? 下面给出:

用户:活动记录模型 学生:活动记录模型,继承自用户 (STI) 教师:活动记录模型,继承自用户 (STI) 项目:活动记录模型

这里有一些例子:

FIRST CASE(更多 STI 关联)

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 自动使用别名 teachers_projects。如何覆盖它,以便我可以在 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')

第二种情况(一个 STI 协会)

如果我在方法includes() 中仅使用:students(不使用:teachers),Rails 会为:students 使用STI 基类名称users(未附加_projects)的名称别名:

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 别名跟踪器

https://github.com/rails/rails/blob/v4.2.0/activerecord/lib/active_record/associations/alias_tracker.rb#L59

测试应用

https://gist.github.com/phlegx/add77d24ebc57f211e8b

https://github.com/phlegx/rails_query_alias_names

【问题讨论】:

你有没有尝试过类似 Project.joins(:teachers).where...... 如果你得到答案,请通知我,这样我也能看到答案。 您能否提供一个 Gist 以显示模型定义、迁移和一些数据。以便我可以设置并尝试做? @phlegx 谢谢.. 让我们看看我能不能帮你。 :-) @phlegx 你的项目在公共 GitHub 上吗? 【参考方案1】:

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 作为参数的 Class 方法中会使其更加整洁和可重用,因为这在控制器中看起来有点混乱。

【讨论】:

【参考方案2】:

我将采用另一种方法来解决这个问题:我不会尝试使用 .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")

相关问题:

ActiveRecord query with alias'd table names(我第一次使用这种方法的地方)。 Join the same table twice with conditions

【讨论】:

我在您提到的一个问题中给出了答案。你可以稍微简化一下。 current_scope.tablecurrent_scope.arel.source.left 相同(可能不在 Rails "Arel syntax",则不必获取裸表名称。例如。像这样使用它:at = current_scope.table,然后 at[:the_column_name].ascArel 为你做所有事情。 (也许你必须添加一个.to_sql,但我认为 Rails 会为你处理)

以上是关于Rails 4 - 如何在活动记录查询中为包含()和连接()提供别名的主要内容,如果未能解决你的问题,请参考以下文章

从 SQL 到 Rails 活动记录

可以在控制器操作中使用活动记录查询吗?

在 Rails 中通过其 belongs_to 关系查询记录

在Rails 4.1中,如何通过枚举符号查找记录?

Rails活动记录查询检索IDS数组中包含ALL IDS的所有记录

Ruby on Rails 活动记录查询(.each、.collect、.map ...?)