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

前端 未结 2 1761
日久生厌
日久生厌 2021-02-02 15:08

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

  • User: active record model
  • Student: active record model, inherits from
相关标签:
2条回答
  • 2021-02-02 15:46

    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
    0 讨论(0)
  • 2021-02-02 15:48

    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.

    0 讨论(0)
提交回复
热议问题