Rails 4 - how to give alias names to includes() and joins() in active record querying

喜欢而已 提交于 2019-12-04 08:23:09

问题


How can I give an alias name for e.g. includes()? Following is given:

  • User: active record model
  • Student: active record model, inherits from User (STI)
  • Teacher: active record model, inherits from User (STI)
  • Project: active record model

Here some examples:

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 uses automatically alias name teachers_projects for :teachers in the SQL. How can I overwrite this, so that I can use alias name teachers instead of teachers_projects in the SQL? :students gets alias name 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)

If I use only :students (without :teachers) in method includes(), Rails uses name alias of the STI base class name users (without _projects attached) for :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

Might exist something like:

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


回答1:


Arel does actually have an alias method.

student_alias = Student.arel_table.alias(:my_student_table_alias)

caveat: this will require you to use even more handcrafted Arel and do the join manually. And the joins in Arel can get a bit complicated if you're not used to them.

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(...)

Something like this should do it. Of course putting this into some Class method with :my_student_table_alias as parameter would make it more tidy and reusable as this would look a bit messy in a controller.




回答2:


I'm going to take another approach to this issue: instead of trying to control the alias names on your queries with an .alias method, I'll let Rails / Arel handle that and just look the correct table name (aliased or not) up whenever there is need for it within a scope.

Add this helper method to your model, that you'd be able to call from an scope to know if the scope is being used within a JOIN that has the table name aliased (multiple joins on the same table), or if on the other hand the scope has no alias for the table name.

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

This uses current_scope as the base object to look for the arel table. I'm calling source on that object to obtain an Arel::SelectManager that in turn will give you the current table on the #left. There are two options there: either you have there an Arel::Table (no alias, table name is on #name) or you have an Arel::Nodes::TableAlias with the alias on its #right.

Now you only need to use that on your order statements (untested):

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

Related questions:

  • ActiveRecord query with alias'd table names (where I first used this approach).
  • Join the same table twice with conditions


来源:https://stackoverflow.com/questions/28595636/rails-4-how-to-give-alias-names-to-includes-and-joins-in-active-record-que

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!