Changeset 4022
- Timestamp:
- 03/24/06 14:46:17 (2 years ago)
- Files:
-
- trunk/activerecord/CHANGELOG (modified) (1 diff)
- trunk/activerecord/lib/active_record/associations.rb (modified) (10 diffs)
- trunk/activerecord/lib/active_record/associations/has_many_through_association.rb (modified) (1 diff)
- trunk/activerecord/lib/active_record/reflection.rb (modified) (2 diffs)
- trunk/activerecord/test/associations_cascaded_eager_loading_test.rb (modified) (2 diffs)
- trunk/activerecord/test/fixtures/author.rb (modified) (1 diff)
- trunk/activerecord/test/fixtures/post.rb (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/activerecord/CHANGELOG
r4013 r4022 1 1 *SVN* 2 3 * Change has_many :through to use the :source option to specify the source association. :class_name is now ignored. [Rick Olson] 4 5 class Connection < ActiveRecord::Base 6 belongs_to :user 7 belongs_to :channel 8 end 9 10 class Channel < ActiveRecord::Base 11 has_many :connections 12 has_many :contacts, :through => :connections, :class_name => 'User' # OLD 13 has_many :contacts, :through => :connections, :source => :user # NEW 14 end 2 15 3 16 * Fixed DB2 adapter so nullable columns will be determines correctly now and quotes from column default values will be removed #4350 [contact@maik-schmidt.de] trunk/activerecord/lib/active_record/associations.rb
r4015 r4022 16 16 17 17 def message 18 "Could not find the association '#{@reflection.options[:through]}'in model #{@reflection.klass}"18 "Could not find the association #{@reflection.options[:through].inspect} in model #{@reflection.klass}" 19 19 end 20 20 end … … 33 33 34 34 class HasManyThroughSourceAssociationNotFoundError < ActiveRecordError 35 def initialize(through_reflection, source_reflection_names) 36 @through_reflection = through_reflection 37 @source_reflection_names = source_reflection_names 35 def initialize(reflection) 36 @reflection = reflection 37 @through_reflection = reflection.through_reflection 38 @source_reflection_names = reflection.source_reflection_names 39 @source_associations = reflection.through_reflection.klass.reflect_on_all_associations.collect { |a| a.name.inspect } 38 40 end 39 41 40 42 def message 41 "Could not find the source association s #{@source_reflection_names.to_sentence} in model #{@through_reflection.klass}"43 "Could not find the source association(s) #{@source_reflection_names.collect(&:inspect).to_sentence :connector => 'or'} in model #{@through_reflection.klass}. Try 'has_many #{@reflection.name.inspect}, :through => #{@through_reflection.name.inspect}, :source => <name>'. Is it one of #{@source_associations.to_sentence :connector => 'or'}?" 42 44 end 43 45 end … … 49 51 50 52 def message 51 "Can not eagerly load the polymorphic association '#{@reflection.name}'"53 "Can not eagerly load the polymorphic association #{@reflection.name.inspect}" 52 54 end 53 55 end … … 202 204 # end 203 205 # 206 # == Association Join Models 207 # 208 # Has Many associations can be configured with the :through option to use an explicit join model to retrieve the data. This 209 # operates similarly to a <tt>has_and_belongs_to_many</tt> association. The advantage is that you're able to add validations, 210 # callbacks, and extra attributes on the join model. Consider the following schema: 211 # 212 # class Author < ActiveRecord::Base 213 # has_many :authorships 214 # has_many :books, :through => :authorships 215 # end 216 # 217 # class Authorship < ActiveRecord::Base 218 # belongs_to :author 219 # belongs_to :book 220 # end 221 # 222 # @author = Author.find :first 223 # @author.authorships.collect { |a| a.book } # selects all books that the author's authorships belong to. 224 # @author.books # selects all books by using the Authorship join model 225 # 226 # You can also go through a has_many association on the join model: 227 # 228 # class Firm < ActiveRecord::Base 229 # has_many :clients 230 # has_many :invoices, :through => :clients 231 # end 232 # 233 # class Client < ActiveRecord::Base 234 # belongs_to :firm 235 # has_many :invoices 236 # end 237 # 238 # class Invoice < ActiveRecord::Base 239 # belongs_to :client 240 # end 241 # 242 # @firm = Firm.find :first 243 # @firm.clients.collect { |c| c.invoices }.flatten # select all invoices for all clients of the firm 244 # @firm.invoices # selects all invoices by going through the Client join model. 245 # 204 246 # == Caching 205 247 # … … 264 306 # It's currently not possible to use eager loading on multiple associations from the same table. Eager loading will not pull 265 307 # additional attributes on join tables, so "rich associations" with has_and_belongs_to_many is not a good fit for eager loading. 266 # 308 # 267 309 # == Table Aliasing 268 310 # … … 424 466 # * <tt>:select</tt>: By default, this is * as in SELECT * FROM, but can be changed if you for example want to do a join, but not 425 467 # include the joined columns. 468 # * <tt>:through</tt>: Specifies a Join Model to perform the query through. Options for <tt>:class_name</tt> and <tt>:foreign_key</tt> 469 # are ignored, as the association uses the source reflection. You can only use a <tt>:through</tt> query through a <tt>belongs_to</tt> 470 # or <tt>has_many</tt> association. 471 # * <tt>:source</tt>: Specifies the source association name used by <tt>has_many :through</tt> queries. Only use it if the name cannot be 472 # inferred from the association. <tt>has_many :subscribers, :through => :subscriptions</tt> will look for either +:subscribers+ or 473 # +:subscriber+ on +Subscription+, unless a +:source+ is given. 426 474 # 427 475 # Option examples: … … 431 479 # has_many :tracks, :order => "position", :dependent => :destroy 432 480 # has_many :comments, :dependent => :nullify 481 # has_many :subscribers, :through => :subscriptions, :source => :user 433 482 # has_many :subscribers, :class_name => "Person", :finder_sql => 434 483 # 'SELECT DISTINCT people.* ' + … … 436 485 # 'WHERE ps.post_id = #{id} AND ps.person_id = p.id ' + 437 486 # 'ORDER BY p.first_name' 487 # 488 # Specifying the :through option 489 # 438 490 def has_many(association_id, options = {}, &extension) 439 491 reflection = create_has_many_reflection(association_id, options, &extension) … … 954 1006 :exclusively_dependent, :dependent, 955 1007 :select, :conditions, :include, :order, :group, :limit, :offset, 956 :as, :through, 1008 :as, :through, :source, 957 1009 :finder_sql, :counter_sql, 958 1010 :before_add, :after_add, :before_remove, :after_remove, … … 1321 1373 1322 1374 if reflection.macro == :has_and_belongs_to_many || (reflection.macro == :has_many && reflection.options[:through]) 1323 @aliased_join_table_name = reflection.macro == :has_and_belongs_to_many ? reflection.options[:join_table] : parent.active_record.reflect_on_association(reflection.options[:through]).klass.table_name1375 @aliased_join_table_name = reflection.macro == :has_and_belongs_to_many ? reflection.options[:join_table] : reflection.through_reflection.klass.table_name 1324 1376 unless join_dependency.table_aliases[aliased_join_table_name].zero? 1325 1377 @aliased_join_table_name = active_record.connection.table_alias_for "#{pluralize(reflection.name)}_#{parent_table_name}_join" trunk/activerecord/lib/active_record/associations/has_many_through_association.rb
r4007 r4022 73 73 "#{@reflection.through_reflection.table_name}.#{@reflection.through_reflection.primary_key_name} = #{@owner.quoted_id}" 74 74 else 75 raise ActiveRecordError, "Invalid source reflection macro :#{@reflection.source_reflection.macro} for has_many #{@reflection.name}, :through => #{@reflection.through_reflection.name} "75 raise ActiveRecordError, "Invalid source reflection macro :#{@reflection.source_reflection.macro} for has_many #{@reflection.name}, :through => #{@reflection.through_reflection.name}. Use :source to specify the source reflection." 76 76 end 77 77 end trunk/activerecord/lib/active_record/reflection.rb
r3973 r4022 145 145 end 146 146 147 # Gets an array of possible :through reflection names147 # Gets an array of possible :through source reflection names 148 148 # 149 149 # [singularized, pluralized] 150 150 def source_reflection_names 151 @source_reflection_names ||= (options[:class_name] ? 152 [options[:class_name].underscore, options[:class_name].underscore.pluralize] : 153 [name.to_s.singularize, name] 154 ).collect { |n| n.to_sym } 151 @source_reflection_names ||= (options[:source] ? [options[:source]] : [name.to_s.singularize, name]).collect { |n| n.to_sym } 155 152 end 156 153 … … 174 171 175 172 if source_reflection.nil? 176 raise HasManyThroughSourceAssociationNotFoundError.new( through_reflection, source_reflection_names)173 raise HasManyThroughSourceAssociationNotFoundError.new(self) 177 174 end 178 175 trunk/activerecord/test/associations_cascaded_eager_loading_test.rb
r3921 r4022 88 88 89 89 def test_eager_association_loading_with_multiple_stis_and_order 90 author = Author.find(:first, :include => { :posts => [ :special_comments , :very_special_comment ] }, :order => 'authors.name, special_comments.body, very_special_comments.body', :conditions => 'posts.id = 4')90 author = Author.find(:first, :include => { :posts => [ :special_comments , :very_special_comment ] }, :order => 'authors.name, comments.body, very_special_comments_posts.body', :conditions => 'posts.id = 4') 91 91 assert_equal authors(:david), author 92 92 assert_no_queries do … … 97 97 98 98 def test_eager_association_loading_of_stis_with_multiple_references 99 authors = Author.find(:all, :include => { :posts => { :special_comments => { :post => [ :special_comments, :very_special_comment ] } } }, :order => ' special_comments.body, very_special_comments.body', :conditions => 'posts.id = 4')99 authors = Author.find(:all, :include => { :posts => { :special_comments => { :post => [ :special_comments, :very_special_comment ] } } }, :order => 'comments.body, very_special_comments_posts.body', :conditions => 'posts.id = 4') 100 100 assert_equal [authors(:david)], authors 101 101 assert_no_queries do trunk/activerecord/test/fixtures/author.rb
r4006 r4022 5 5 has_many :posts_with_comments_and_categories, :include => [ :comments, :categories ], :order => "posts.id", :class_name => "Post" 6 6 has_many :comments, :through => :posts 7 has_many :funky_comments, :through => :posts, : class_name => 'Comment'7 has_many :funky_comments, :through => :posts, :source => :comments 8 8 9 9 has_many :special_posts, :class_name => "Post" trunk/activerecord/test/fixtures/post.rb
r4007 r4022 29 29 end 30 30 31 has_many :funky_tags, :through => :taggings, : class_name => 'Tag'31 has_many :funky_tags, :through => :taggings, :source => :tag 32 32 has_many :super_tags, :through => :taggings 33 33 has_one :tagging, :as => :taggable 34 34 35 35 has_many :invalid_taggings, :as => :taggable, :class_name => "Tagging", :conditions => 'taggings.id < 0' 36 has_many :invalid_tags, :through => :invalid_taggings, : class_name => "Tag"36 has_many :invalid_tags, :through => :invalid_taggings, :source => :tag 37 37 38 38 has_many :categorizations, :foreign_key => :category_id