@@ -20,7 +20,13 @@ def initialize(owner_class_name, reflection, source_reflection)
20
20
super ( "Cannot have a has_many :through association '#{ owner_class_name } ##{ reflection . name } ' on the polymorphic object '#{ source_reflection . class_name } ##{ source_reflection . name } '." )
21
21
end
22
22
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
+
24
30
class HasManyThroughSourceAssociationNotFoundError < ActiveRecordError #:nodoc:
25
31
def initialize ( reflection )
26
32
through_reflection = reflection . through_reflection
@@ -529,6 +535,8 @@ module ClassMethods
529
535
# * <tt>:source</tt>: Specifies the source association name used by <tt>has_many :through</tt> queries. Only use it if the name cannot be
530
536
# inferred from the association. <tt>has_many :subscribers, :through => :subscriptions</tt> will look for either +:subscribers+ or
531
537
# +: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.
532
540
# * <tt>:uniq</tt> - if set to true, duplicates will be omitted from the collection. Useful in conjunction with :through.
533
541
#
534
542
# Option examples:
@@ -1087,7 +1095,7 @@ def create_has_many_reflection(association_id, options, &extension)
1087
1095
:class_name , :table_name , :foreign_key ,
1088
1096
:exclusively_dependent , :dependent ,
1089
1097
:select , :conditions , :include , :order , :group , :limit , :offset ,
1090
- :as , :through , :source ,
1098
+ :as , :through , :source , :source_type ,
1091
1099
:uniq ,
1092
1100
:finder_sql , :counter_sql ,
1093
1101
:before_add , :after_add , :before_remove , :after_remove ,
@@ -1491,57 +1499,65 @@ def association_join
1491
1499
case
1492
1500
when reflection . macro == :has_many && reflection . options [ :through ]
1493
1501
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
+
1494
1506
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 )
1505
1511
]
1506
1512
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" ) ,
1517
1523
klass . quote_value ( source_reflection . active_record . base_class . name )
1518
1524
]
1519
1525
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 ] )
1542
1543
]
1544
+ else
1545
+ second_key = source_reflection . options [ :foreign_key ] || klass . to_s . classify . foreign_key
1543
1546
end
1544
1547
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
+ ]
1545
1561
1546
1562
when reflection . macro == :has_many && reflection . options [ :as ]
1547
1563
" LEFT OUTER JOIN %s ON %s.%s = %s.%s AND %s.%s = %s" % [
@@ -1588,6 +1604,7 @@ def association_join
1588
1604
end
1589
1605
1590
1606
protected
1607
+
1591
1608
def pluralize ( table_name )
1592
1609
ActiveRecord ::Base . pluralize_table_names ? table_name . to_s . pluralize : table_name
1593
1610
end
0 commit comments