首页 文章

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

提问于
浏览
12

如何为例如别名命名 includes() ?以下是:

  • 用户:活动记录模型

  • 学生:活动记录模型,继承自用户(STI)

  • 老师:活动记录模型,继承自User(STI)

  • 项目:活动记录模型

这里有一些例子:

FIRST CASE (more STI associations)

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 .

This examples fails:

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')

SECOND CASE (one STI association)

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

Project.all.includes(:students).order('users.name ASC') # order on students

This examples fails:

Project.all.includes(:students).order('students.name ASC')
Project.all.includes(:students).order('students_projects.name ASC')

QUESTION

可能存在类似于:

Project.all.includes(:students).alias(students: :my_alias)

RAILS ALIAS TRACKER

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

TESTING APP

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

https://github.com/phlegx/rails_query_alias_names

2 回答

  • 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方法会使它更整洁和可重用,因为这在控制器中看起来有点乱 .

  • 3

    我将采用另一种方法来解决这个问题:我不会尝试使用 .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 上有别名 Arel::Nodes::TableAlias .

    现在你只需要在 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")
    

    相关问题:

相关问题