Ticket #6461: arbitrary_has_many_through.diff
| File arbitrary_has_many_through.diff, 8.7 kB (added by obrie, 2 years ago) |
|---|
-
C:/Projects/workspace/rails/activerecord/lib/active_record/associations/has_many_through_association.rb
old new 155 155 156 156 # Build SQL conditions from attributes, qualified by table name. 157 157 def construct_conditions 158 table_name = @reflection.through_reflection.table_name 159 conditions = construct_quoted_owner_attributes(@reflection.through_reflection).map do |attr, value| 158 reflection, table_id = find_deepest_through(@reflection.through_reflection) 159 table_name = reflection.table_name 160 table_name += "_#{table_id}" if table_id 161 162 conditions = construct_quoted_owner_attributes(reflection).map do |attr, value| 160 163 "#{table_name}.#{attr} = #{value}" 161 164 end 162 165 conditions << sql_conditions if sql_conditions … … 163 166 "(" + conditions.join(') AND (') + ")" 164 167 end 165 168 169 def find_deepest_through(reflection, table_ids = {@reflection.table_name => 1, reflection.table_name => 1}) 170 # Don't overwrite the original hash since this only a look-ahead 171 table_ids = table_ids.dup 172 173 if through_reflection = reflection.through_reflection 174 table_name = through_reflection.table_name 175 table_ids[table_name] = (table_ids[table_name] || 0) + 1 176 177 find_deepest_through(through_reflection, table_ids) 178 else 179 table_id = table_ids[reflection.table_name] || 1 180 return reflection, (table_id > 1 ? table_id : nil) 181 end 182 end 183 166 184 def construct_from 167 185 @reflection.table_name 168 186 end … … 171 189 selected = custom_select || @reflection.options[:select] || "#{@reflection.table_name}.*" 172 190 end 173 191 174 def construct_joins(custom_joins = nil) 192 def construct_joins(custom_joins = nil, reflection = @reflection, table_ids = {@reflection.table_name => 1}) 193 prepended_joins = '' 194 appended_joins = '' 195 196 through_reflection = reflection.through_reflection 197 source_reflection = reflection.source_reflection 198 reflection_table_name = reflection.table_name 199 parent_table_id = table_ids[reflection_table_name] 200 201 through_table_name = through_reflection.table_name 202 through_table_name_alias = through_table_name 203 if table_ids[through_table_name] 204 table_id = table_ids[through_table_name] += 1 205 through_table_name_alias += "_#{table_id}" 206 else 207 table_ids[through_table_name] = 1 208 end 209 210 if through_reflection.through_reflection 211 appended_joins = ' ' + construct_joins(nil, through_reflection, table_ids) 212 elsif source_reflection.through_reflection 213 source_reflection, parent_table_id = find_deepest_through(source_reflection, table_ids) 214 reflection_table_name = source_reflection.table_name 215 216 prepended_joins = construct_joins(nil, reflection.source_reflection, table_ids) + ' ' 217 end 218 reflection_table_name += "_#{parent_table_id}" if parent_table_id && parent_table_id > 1 219 175 220 polymorphic_join = nil 176 if @reflection.through_reflection.options[:as] || @reflection.source_reflection.macro == :belongs_to177 reflection_primary_key = @reflection.klass.primary_key178 source_primary_key = @reflection.source_reflection.primary_key_name221 if through_reflection.options[:as] || source_reflection.macro == :belongs_to 222 reflection_primary_key = reflection.klass.primary_key 223 source_primary_key = source_reflection.primary_key_name 179 224 else 180 reflection_primary_key = @reflection.source_reflection.primary_key_name 181 source_primary_key = @reflection.klass.primary_key 182 if @reflection.source_reflection.options[:as] 225 reflection_primary_key = source_reflection.primary_key_name 226 source_primary_key = reflection.klass.primary_key 227 228 if source_reflection.options[:as] 183 229 polymorphic_join = "AND %s.%s = %s" % [ 184 @reflection.table_name, "#{@reflection.source_reflection.options[:as]}_type",185 @owner.class.quote_value(@reflection.through_reflection.klass.name)230 reflection_table_name, "#{source_reflection.options[:as]}_type", 231 ActiveRecord::Base.quote_value(through_reflection.klass.name) 186 232 ] 187 233 end 188 234 end … … 187 233 end 188 234 end 189 235 190 "INNER JOIN %s ON %s.%s = %s.%s %s #{@reflection.options[:joins]} #{custom_joins}" % [ 191 @reflection.through_reflection.table_name, 192 @reflection.table_name, reflection_primary_key, 193 @reflection.through_reflection.table_name, source_primary_key, 236 "#{prepended_joins}INNER JOIN %s %sON %s.%s = %s.%s %s #{reflection.options[:joins]} #{custom_joins}#{appended_joins}" % [ 237 through_table_name, 238 through_table_name != through_table_name_alias ? "AS #{through_table_name_alias} " : '', 239 reflection_table_name, reflection_primary_key, 240 through_table_name_alias, source_primary_key, 194 241 polymorphic_join 195 242 ] 196 243 end -
C:/Projects/workspace/rails/activerecord/lib/active_record/reflection.rb
old new 168 168 if through_reflection.nil? 169 169 raise HasManyThroughAssociationNotFoundError.new(active_record.name, self) 170 170 end 171 171 172 172 if source_reflection.nil? 173 173 raise HasManyThroughSourceAssociationNotFoundError.new(self) 174 174 end 175 175 176 176 if source_reflection.options[:polymorphic] 177 177 raise HasManyThroughAssociationPolymorphicError.new(active_record.name, self, source_reflection) 178 178 end 179 180 unless [:belongs_to, :has_many].include?(source_reflection.macro) && source_reflection.options[:through].nil?179 180 unless [:belongs_to, :has_many].include?(source_reflection.macro) 181 181 raise HasManyThroughSourceAssociationMacroError.new(self) 182 182 end 183 183 end -
C:/Projects/workspace/rails/activerecord/test/associations/join_model_test.rb
old new 336 336 end 337 337 end 338 338 339 def test_has_many_through_has_many_through 340 assert_raise(ActiveRecord::HasManyThroughSourceAssociationMacroError) { authors(:david).tags } 339 def test_has_many_through_multiple_levels 340 author = authors(:david) 341 342 assert_equal [tags(:general)], author.tags.uniq.sort_by { |t| t.id } 343 assert_equal [taggings(:welcome_general), taggings(:thinking_general), taggings(:fake)], author.tag_taggings.uniq.sort_by { |t| t.id } 344 assert_equal [tags(:general)], author.tag_tagging_tags.uniq.sort_by { |t| t.id } 341 345 end 342 346 343 347 def test_has_many_through_habtm -
C:/Projects/workspace/rails/activerecord/test/fixtures/author.rb
old new 56 56 57 57 has_many :tagging, :through => :posts # through polymorphic has_one 58 58 has_many :taggings, :through => :posts, :source => :taggings # through polymorphic has_many 59 has_many :tags, :through => :posts # through has_many :through 59 has_many :tags, :through => :posts # through remote has_many :through 60 has_many :tag_taggings, :through => :tags, :source => :taggings # through 2 local has_many :through, 1 remote 61 has_many :tag_tagging_tags, :through => :tag_taggings, :source => :tag # multiple table references 62 60 63 has_many :post_categories, :through => :posts, :source => :categories 61 64 62 65 belongs_to :author_address