@@ -20,7 +20,13 @@ def initialize(owner_class_name, reflection, source_reflection)
2020 super ( "Cannot have a has_many :through association '#{ owner_class_name } ##{ reflection . name } ' on the polymorphic object '#{ source_reflection . class_name } ##{ source_reflection . name } '." )
2121 end
2222 end
23-
23+
24+ class HasManyThroughAssociationPointlessSourceTypeError < ActiveRecordError #:nodoc:
25+ def initialize ( owner_class_name , reflection , source_reflection )
26+ super ( "Cannot have a has_many :through association '#{ owner_class_name } ##{ reflection . name } ' with a :source_type option if the '#{ reflection . through_reflection . class_name } ##{ source_reflection . name } ' is not polymorphic. Try removing :source_type on your association." )
27+ end
28+ end
29+
2430 class HasManyThroughSourceAssociationNotFoundError < ActiveRecordError #:nodoc:
2531 def initialize ( reflection )
2632 through_reflection = reflection . through_reflection
@@ -529,6 +535,8 @@ module ClassMethods
529535 # * <tt>:source</tt>: Specifies the source association name used by <tt>has_many :through</tt> queries. Only use it if the name cannot be
530536 # inferred from the association. <tt>has_many :subscribers, :through => :subscriptions</tt> will look for either +:subscribers+ or
531537 # +:subscriber+ on +Subscription+, unless a +:source+ is given.
538+ # * <tt>:source_type</tt>: Specifies type of the source association used by <tt>has_many :through</tt> queries where the source association
539+ # is a polymorphic belongs_to.
532540 # * <tt>:uniq</tt> - if set to true, duplicates will be omitted from the collection. Useful in conjunction with :through.
533541 #
534542 # Option examples:
@@ -1087,7 +1095,7 @@ def create_has_many_reflection(association_id, options, &extension)
10871095 :class_name , :table_name , :foreign_key ,
10881096 :exclusively_dependent , :dependent ,
10891097 :select , :conditions , :include , :order , :group , :limit , :offset ,
1090- :as , :through , :source ,
1098+ :as , :through , :source , :source_type ,
10911099 :uniq ,
10921100 :finder_sql , :counter_sql ,
10931101 :before_add , :after_add , :before_remove , :after_remove ,
@@ -1491,57 +1499,65 @@ def association_join
14911499 case
14921500 when reflection . macro == :has_many && reflection . options [ :through ]
14931501 through_conditions = through_reflection . options [ :conditions ] ? "AND #{ interpolate_sql ( sanitize_sql ( through_reflection . options [ :conditions ] ) ) } " : ''
1502+
1503+ jt_foreign_key = jt_as_extra = jt_source_extra = jt_sti_extra = nil
1504+ first_key = second_key = as_extra = nil
1505+
14941506 if through_reflection . options [ :as ] # has_many :through against a polymorphic join
1495- polymorphic_foreign_key = through_reflection . options [ :as ] . to_s + '_id'
1496- polymorphic_foreign_type = through_reflection . options [ :as ] . to_s + '_type'
1497-
1498- " LEFT OUTER JOIN %s ON (%s.%s = %s.%s AND %s.%s = %s) " % [
1499- table_alias_for ( through_reflection . klass . table_name , aliased_join_table_name ) ,
1500- aliased_join_table_name , polymorphic_foreign_key ,
1501- parent . aliased_table_name , parent . primary_key ,
1502- aliased_join_table_name , polymorphic_foreign_type , klass . quote_value ( parent . active_record . base_class . name ) ] +
1503- " LEFT OUTER JOIN %s ON %s.%s = %s.%s " % [ table_name_and_alias ,
1504- aliased_table_name , primary_key , aliased_join_table_name , options [ :foreign_key ] || reflection . klass . to_s . classify . foreign_key
1507+ jt_foreign_key = through_reflection . options [ :as ] . to_s + '_id'
1508+ jt_as_extra = " AND %s.%s = %s" % [
1509+ aliased_join_table_name , reflection . active_record . connection . quote_column_name ( through_reflection . options [ :as ] . to_s + '_type' ) ,
1510+ klass . quote_value ( parent . active_record . base_class . name )
15051511 ]
15061512 else
1507- if source_reflection . macro == :has_many && source_reflection . options [ :as ]
1508- " LEFT OUTER JOIN %s ON %s.%s = %s.%s " % [
1509- table_alias_for ( through_reflection . klass . table_name , aliased_join_table_name ) , aliased_join_table_name ,
1510- through_reflection . primary_key_name ,
1511- parent . aliased_table_name , parent . primary_key ] +
1512- " LEFT OUTER JOIN %s ON %s.%s = %s.%s AND %s.%s = %s " % [
1513- table_name_and_alias ,
1514- aliased_table_name , " #{ source_reflection . options [ :as ] } _id" ,
1515- aliased_join_table_name , options [ :foreign_key ] || primary_key ,
1516- aliased_table_name , "#{ source_reflection . options [ :as ] } _type" ,
1513+ jt_foreign_key = through_reflection . primary_key_name
1514+ end
1515+
1516+ case source_reflection . macro
1517+ when :has_many
1518+ if source_reflection . options [ :as ]
1519+ first_key = " #{ source_reflection . options [ :as ] } _id"
1520+ second_key = options [ :foreign_key ] || primary_key
1521+ as_extra = " AND %s.%s = %s" % [
1522+ aliased_table_name , reflection . active_record . connection . quote_column_name ( "#{ source_reflection . options [ :as ] } _type" ) ,
15171523 klass . quote_value ( source_reflection . active_record . base_class . name )
15181524 ]
15191525 else
1520- case source_reflection . macro
1521- when :belongs_to
1522- first_key = primary_key
1523- second_key = source_reflection . options [ :foreign_key ] || klass . to_s . classify . foreign_key
1524- extra = nil
1525- when :has_many
1526- first_key = through_reflection . klass . base_class . to_s . classify . foreign_key
1527- second_key = options [ :foreign_key ] || primary_key
1528- extra = through_reflection . klass . descends_from_active_record? ? nil :
1529- " AND %s.%s = %s" % [
1530- aliased_join_table_name ,
1531- reflection . active_record . connection . quote_column_name ( through_reflection . active_record . inheritance_column ) ,
1532- through_reflection . klass . quote_value ( through_reflection . klass . name . demodulize ) ]
1533- end
1534- " LEFT OUTER JOIN %s ON (%s.%s = %s.%s%s) " % [
1535- table_alias_for ( through_reflection . klass . table_name , aliased_join_table_name ) ,
1536- aliased_join_table_name , through_reflection . primary_key_name ,
1537- parent . aliased_table_name , parent . primary_key , extra ] +
1538- " LEFT OUTER JOIN %s ON (%s.%s = %s.%s) " % [
1539- table_name_and_alias ,
1540- aliased_table_name , first_key ,
1541- aliased_join_table_name , second_key
1526+ first_key = through_reflection . klass . base_class . to_s . classify . foreign_key
1527+ second_key = options [ :foreign_key ] || primary_key
1528+ end
1529+
1530+ unless through_reflection . klass . descends_from_active_record?
1531+ jt_sti_extra = " AND %s.%s = %s" % [
1532+ aliased_join_table_name ,
1533+ reflection . active_record . connection . quote_column_name ( through_reflection . active_record . inheritance_column ) ,
1534+ through_reflection . klass . quote_value ( through_reflection . klass . name . demodulize ) ]
1535+ end
1536+ when :belongs_to
1537+ first_key = primary_key
1538+ if reflection . options [ :source_type ]
1539+ second_key = source_reflection . association_foreign_key
1540+ jt_source_extra = " AND %s.%s = %s" % [
1541+ aliased_join_table_name , reflection . active_record . connection . quote_column_name ( reflection . source_reflection . options [ :foreign_type ] ) ,
1542+ klass . quote_value ( reflection . options [ :source_type ] )
15421543 ]
1544+ else
1545+ second_key = source_reflection . options [ :foreign_key ] || klass . to_s . classify . foreign_key
15431546 end
15441547 end
1548+
1549+ " LEFT OUTER JOIN %s ON (%s.%s = %s.%s%s%s%s) " % [
1550+ table_alias_for ( through_reflection . klass . table_name , aliased_join_table_name ) ,
1551+ parent . aliased_table_name , reflection . active_record . connection . quote_column_name ( parent . primary_key ) ,
1552+ aliased_join_table_name , reflection . active_record . connection . quote_column_name ( jt_foreign_key ) ,
1553+ jt_as_extra , jt_source_extra , jt_sti_extra
1554+ ] +
1555+ " LEFT OUTER JOIN %s ON (%s.%s = %s.%s%s) " % [
1556+ table_name_and_alias ,
1557+ aliased_table_name , reflection . active_record . connection . quote_column_name ( first_key ) ,
1558+ aliased_join_table_name , reflection . active_record . connection . quote_column_name ( second_key ) ,
1559+ as_extra
1560+ ]
15451561
15461562 when reflection . macro == :has_many && reflection . options [ :as ]
15471563 " LEFT OUTER JOIN %s ON %s.%s = %s.%s AND %s.%s = %s" % [
@@ -1588,6 +1604,7 @@ def association_join
15881604 end
15891605
15901606 protected
1607+
15911608 def pluralize ( table_name )
15921609 ActiveRecord ::Base . pluralize_table_names ? table_name . to_s . pluralize : table_name
15931610 end
0 commit comments