LEFT OUTER JOIN in Rails 4

前端 未结 12 1726
一整个雨季
一整个雨季 2020-11-28 09:44

I have 3 models:

class Student < ActiveRecord::Base
  has_many :student_enrollments, dependent: :destroy
  has_many :courses, through: :student_enrollment         


        
12条回答
  •  清酒与你
    2020-11-28 10:07

    I've been struggling with this kind of problem for quite some while, and decided to do something to solve it once and for all. I published a Gist that addresses this issue: https://gist.github.com/nerde/b867cd87d580e97549f2

    I created a little AR hack that uses Arel Table to dynamically build the left joins for you, without having to write raw SQL in your code:

    class ActiveRecord::Base
      # Does a left join through an association. Usage:
      #
      #     Book.left_join(:category)
      #     # SELECT "books".* FROM "books"
      #     # LEFT OUTER JOIN "categories"
      #     # ON "books"."category_id" = "categories"."id"
      #
      # It also works through association's associations, like `joins` does:
      #
      #     Book.left_join(category: :master_category)
      def self.left_join(*columns)
        _do_left_join columns.compact.flatten
      end
    
      private
    
      def self._do_left_join(column, this = self) # :nodoc:
        collection = self
        if column.is_a? Array
          column.each do |col|
            collection = collection._do_left_join(col, this)
          end
        elsif column.is_a? Hash
          column.each do |key, value|
            assoc = this.reflect_on_association(key)
            raise "#{this} has no association: #{key}." unless assoc
            collection = collection._left_join(assoc)
            collection = collection._do_left_join value, assoc.klass
          end
        else
          assoc = this.reflect_on_association(column)
          raise "#{this} has no association: #{column}." unless assoc
          collection = collection._left_join(assoc)
        end
        collection
      end
    
      def self._left_join(assoc) # :nodoc:
        source = assoc.active_record.arel_table
        pk = assoc.association_primary_key.to_sym
        joins source.join(assoc.klass.arel_table,
          Arel::Nodes::OuterJoin).on(source[assoc.foreign_key].eq(
            assoc.klass.arel_table[pk])).join_sources
      end
    end
    

    Hope it helps.

提交回复
热议问题