Created
March 24, 2010 04:06
-
-
Save jasoncodes/341979 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
raise "Check if this is still needed on #{Rails.version}" unless Rails.version == '2.3.5' | |
# <https://rails.lighthouseapp.com/projects/8994/tickets/3208-belongs_to-with-primary_key-does-not-work-with-finds-include> | |
# this should hopefully be fixed in 2.3.6. Untested on 3.0. | |
# can't easily patch the output of association_join via a alias_method_chain | |
# so the whole method is replaced here with the one line fix applied | |
module ActiveRecord::Associations::ClassMethods | |
class JoinDependency | |
class JoinAssociation | |
def association_join | |
connection = reflection.active_record.connection | |
join = case reflection.macro | |
when :has_and_belongs_to_many | |
" #{join_type} %s ON %s.%s = %s.%s " % [ | |
table_alias_for(options[:join_table], aliased_join_table_name), | |
connection.quote_table_name(aliased_join_table_name), | |
options[:foreign_key] || reflection.active_record.to_s.foreign_key, | |
connection.quote_table_name(parent.aliased_table_name), | |
reflection.active_record.primary_key] + | |
" #{join_type} %s ON %s.%s = %s.%s " % [ | |
table_name_and_alias, | |
connection.quote_table_name(aliased_table_name), | |
klass.primary_key, | |
connection.quote_table_name(aliased_join_table_name), | |
options[:association_foreign_key] || klass.to_s.foreign_key | |
] | |
when :has_many, :has_one | |
case | |
when reflection.options[:through] | |
through_conditions = through_reflection.options[:conditions] ? "AND #{interpolate_sql(sanitize_sql(through_reflection.options[:conditions]))}" : '' | |
jt_foreign_key = jt_as_extra = jt_source_extra = jt_sti_extra = nil | |
first_key = second_key = as_extra = nil | |
if through_reflection.options[:as] # has_many :through against a polymorphic join | |
jt_foreign_key = through_reflection.options[:as].to_s + '_id' | |
jt_as_extra = " AND %s.%s = %s" % [ | |
connection.quote_table_name(aliased_join_table_name), | |
connection.quote_column_name(through_reflection.options[:as].to_s + '_type'), | |
klass.quote_value(parent.active_record.base_class.name) | |
] | |
else | |
jt_foreign_key = through_reflection.primary_key_name | |
end | |
case source_reflection.macro | |
when :has_many | |
if source_reflection.options[:as] | |
first_key = "#{source_reflection.options[:as]}_id" | |
second_key = options[:foreign_key] || primary_key | |
as_extra = " AND %s.%s = %s" % [ | |
connection.quote_table_name(aliased_table_name), | |
connection.quote_column_name("#{source_reflection.options[:as]}_type"), | |
klass.quote_value(source_reflection.active_record.base_class.name) | |
] | |
else | |
first_key = through_reflection.klass.base_class.to_s.foreign_key | |
second_key = options[:foreign_key] || primary_key | |
end | |
unless through_reflection.klass.descends_from_active_record? | |
jt_sti_extra = " AND %s.%s = %s" % [ | |
connection.quote_table_name(aliased_join_table_name), | |
connection.quote_column_name(through_reflection.active_record.inheritance_column), | |
through_reflection.klass.quote_value(through_reflection.klass.sti_name)] | |
end | |
when :belongs_to | |
first_key = primary_key | |
if reflection.options[:source_type] | |
second_key = source_reflection.association_foreign_key | |
jt_source_extra = " AND %s.%s = %s" % [ | |
connection.quote_table_name(aliased_join_table_name), | |
connection.quote_column_name(reflection.source_reflection.options[:foreign_type]), | |
klass.quote_value(reflection.options[:source_type]) | |
] | |
else | |
second_key = source_reflection.primary_key_name | |
end | |
end | |
" #{join_type} %s ON (%s.%s = %s.%s%s%s%s) " % [ | |
table_alias_for(through_reflection.klass.table_name, aliased_join_table_name), | |
connection.quote_table_name(parent.aliased_table_name), | |
connection.quote_column_name(parent.primary_key), | |
connection.quote_table_name(aliased_join_table_name), | |
connection.quote_column_name(jt_foreign_key), | |
jt_as_extra, jt_source_extra, jt_sti_extra | |
] + | |
" #{join_type} %s ON (%s.%s = %s.%s%s) " % [ | |
table_name_and_alias, | |
connection.quote_table_name(aliased_table_name), | |
connection.quote_column_name(first_key), | |
connection.quote_table_name(aliased_join_table_name), | |
connection.quote_column_name(second_key), | |
as_extra | |
] | |
when reflection.options[:as] && [:has_many, :has_one].include?(reflection.macro) | |
" #{join_type} %s ON %s.%s = %s.%s AND %s.%s = %s" % [ | |
table_name_and_alias, | |
connection.quote_table_name(aliased_table_name), | |
"#{reflection.options[:as]}_id", | |
connection.quote_table_name(parent.aliased_table_name), | |
parent.primary_key, | |
connection.quote_table_name(aliased_table_name), | |
"#{reflection.options[:as]}_type", | |
klass.quote_value(parent.active_record.base_class.name) | |
] | |
else | |
foreign_key = options[:foreign_key] || reflection.active_record.name.foreign_key | |
" #{join_type} %s ON %s.%s = %s.%s " % [ | |
table_name_and_alias, | |
aliased_table_name, | |
foreign_key, | |
parent.aliased_table_name, | |
reflection.options[:primary_key] || parent.primary_key | |
] | |
end | |
when :belongs_to | |
" #{join_type} %s ON %s.%s = %s.%s " % [ | |
table_name_and_alias, | |
connection.quote_table_name(aliased_table_name), | |
(reflection.options[:primary_key] || reflection.klass.primary_key), # patched line | |
connection.quote_table_name(parent.aliased_table_name), | |
options[:foreign_key] || reflection.primary_key_name | |
] | |
else | |
"" | |
end || '' | |
join << %(AND %s) % [ | |
klass.send(:type_condition, aliased_table_name)] unless klass.descends_from_active_record? | |
[through_reflection, reflection].each do |ref| | |
join << "AND #{interpolate_sql(sanitize_sql(ref.options[:conditions], aliased_table_name))} " if ref && ref.options[:conditions] | |
end | |
join | |
end | |
end | |
end | |
end | |
# the same problem also exists in association preloading | |
module ActiveRecord::AssociationPreload::ClassMethods | |
protected | |
def preload_belongs_to_association(records, reflection, preload_options={}) | |
return if records.first.send("loaded_#{reflection.name}?") | |
options = reflection.options | |
primary_key_name = reflection.primary_key_name | |
if options[:polymorphic] | |
polymorph_type = options[:foreign_type] | |
klasses_and_ids = {} | |
# Construct a mapping from klass to a list of ids to load and a mapping of those ids back to their parent_records | |
records.each do |record| | |
if klass = record.send(polymorph_type) | |
klass_id = record.send(primary_key_name) | |
if klass_id | |
id_map = klasses_and_ids[klass] ||= {} | |
id_list_for_klass_id = (id_map[klass_id.to_s] ||= []) | |
id_list_for_klass_id << record | |
end | |
end | |
end | |
klasses_and_ids = klasses_and_ids.to_a | |
else | |
id_map = {} | |
records.each do |record| | |
key = record.send(primary_key_name) | |
if key | |
mapped_records = (id_map[key.to_s] ||= []) | |
mapped_records << record | |
end | |
end | |
klasses_and_ids = [[reflection.klass.name, id_map]] | |
end | |
klasses_and_ids.each do |klass_and_id| | |
klass_name, id_map = *klass_and_id | |
next if id_map.empty? | |
klass = klass_name.constantize | |
table_name = klass.quoted_table_name | |
primary_key = reflection.options[:primary_key] || klass.primary_key # patched line | |
column_type = klass.columns.detect{|c| c.name == primary_key}.type | |
ids = id_map.keys.map do |id| | |
if column_type == :integer | |
id.to_i | |
elsif column_type == :float | |
id.to_f | |
else | |
id | |
end | |
end | |
conditions = "#{table_name}.#{connection.quote_column_name(primary_key)} #{in_or_equals_for_ids(ids)}" | |
conditions << append_conditions(reflection, preload_options) | |
associated_records = klass.with_exclusive_scope do | |
klass.find(:all, :conditions => [conditions, ids], | |
:include => options[:include], | |
:select => options[:select], | |
:joins => options[:joins], | |
:order => options[:order]) | |
end | |
set_association_single_records(id_map, reflection.name, associated_records, primary_key) | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment