root/trunk/activerecord/lib/active_record/associations.rb
| Revision 9247, 103.3 kB (checked in by pratik, 5 months ago) | |
|---|---|
| |
| Line | |
|---|---|
| 1 | require 'active_record/associations/association_proxy' |
| 2 | require 'active_record/associations/association_collection' |
| 3 | require 'active_record/associations/belongs_to_association' |
| 4 | require 'active_record/associations/belongs_to_polymorphic_association' |
| 5 | require 'active_record/associations/has_one_association' |
| 6 | require 'active_record/associations/has_many_association' |
| 7 | require 'active_record/associations/has_many_through_association' |
| 8 | require 'active_record/associations/has_and_belongs_to_many_association' |
| 9 | require 'active_record/associations/has_one_through_association' |
| 10 | |
| 11 | module ActiveRecord |
| 12 | class HasManyThroughAssociationNotFoundError < ActiveRecordError #:nodoc: |
| 13 | def initialize(owner_class_name, reflection) |
| 14 | super("Could not find the association #{reflection.options[:through].inspect} in model #{owner_class_name}") |
| 15 | end |
| 16 | end |
| 17 | |
| 18 | class HasManyThroughAssociationPolymorphicError < ActiveRecordError #:nodoc: |
| 19 | def initialize(owner_class_name, reflection, source_reflection) |
| 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 | end |
| 22 | end |
| 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 | |
| 30 | class HasManyThroughSourceAssociationNotFoundError < ActiveRecordError #:nodoc: |
| 31 | def initialize(reflection) |
| 32 | through_reflection = reflection.through_reflection |
| 33 | source_reflection_names = reflection.source_reflection_names |
| 34 | source_associations = reflection.through_reflection.klass.reflect_on_all_associations.collect { |a| a.name.inspect } |
| 35 | super("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'}?") |
| 36 | end |
| 37 | end |
| 38 | |
| 39 | class HasManyThroughSourceAssociationMacroError < ActiveRecordError #:nodoc: |
| 40 | def initialize(reflection) |
| 41 | through_reflection = reflection.through_reflection |
| 42 | source_reflection = reflection.source_reflection |
| 43 | super("Invalid source reflection macro :#{source_reflection.macro}#{" :through" if source_reflection.options[:through]} for has_many #{reflection.name.inspect}, :through => #{through_reflection.name.inspect}. Use :source to specify the source reflection.") |
| 44 | end |
| 45 | end |
| 46 | |
| 47 | class HasManyThroughCantAssociateThroughHasManyReflection < ActiveRecordError #:nodoc: |
| 48 | def initialize(owner, reflection) |
| 49 | super("Cannot modify association '#{owner.class.name}##{reflection.name}' because the source reflection class '#{reflection.source_reflection.class_name}' is associated to '#{reflection.through_reflection.class_name}' via :#{reflection.source_reflection.macro}.") |
| 50 | end |
| 51 | end |
| 52 | class HasManyThroughCantAssociateNewRecords < ActiveRecordError #:nodoc: |
| 53 | def initialize(owner, reflection) |
| 54 | super("Cannot associate new records through '#{owner.class.name}##{reflection.name}' on '#{reflection.source_reflection.class_name rescue nil}##{reflection.source_reflection.name rescue nil}'. Both records must have an id in order to create the has_many :through record associating them.") |
| 55 | end |
| 56 | end |
| 57 | |
| 58 | class HasManyThroughCantDissociateNewRecords < ActiveRecordError #:nodoc: |
| 59 | def initialize(owner, reflection) |
| 60 | super("Cannot dissociate new records through '#{owner.class.name}##{reflection.name}' on '#{reflection.source_reflection.class_name rescue nil}##{reflection.source_reflection.name rescue nil}'. Both records must have an id in order to delete the has_many :through record associating them.") |
| 61 | end |
| 62 | end |
| 63 | |
| 64 | class EagerLoadPolymorphicError < ActiveRecordError #:nodoc: |
| 65 | def initialize(reflection) |
| 66 | super("Can not eagerly load the polymorphic association #{reflection.name.inspect}") |
| 67 | end |
| 68 | end |
| 69 | |
| 70 | class ReadOnlyAssociation < ActiveRecordError #:nodoc: |
| 71 | def initialize(reflection) |
| 72 | super("Can not add to a has_many :through association. Try adding to #{reflection.through_reflection.name.inspect}.") |
| 73 | end |
| 74 | end |
| 75 | |
| 76 | module Associations # :nodoc: |
| 77 | def self.included(base) |
| 78 | base.extend(ClassMethods) |
| 79 | end |
| 80 | |
| 81 | # Clears out the association cache |
| 82 | def clear_association_cache #:nodoc: |
| 83 | self.class.reflect_on_all_associations.to_a.each do |assoc| |
| 84 | instance_variable_set "@#{assoc.name}", nil |
| 85 | end unless self.new_record? |
| 86 | end |
| 87 | |
| 88 | # Associations are a set of macro-like class methods for tying objects together through foreign keys. They express relationships like |
| 89 | # "Project has one Project Manager" or "Project belongs to a Portfolio". Each macro adds a number of methods to the class which are |
| 90 | # specialized according to the collection or association symbol and the options hash. It works much the same way as Ruby's own <tt>attr*</tt> |
| 91 | # methods. Example: |
| 92 | # |
| 93 | # class Project < ActiveRecord::Base |
| 94 | # belongs_to :portfolio |
| 95 | # has_one :project_manager |
| 96 | # has_many :milestones |
| 97 | # has_and_belongs_to_many :categories |
| 98 | # end |
| 99 | # |
| 100 | # The project class now has the following methods (and more) to ease the traversal and manipulation of its relationships: |
| 101 | # * <tt>Project#portfolio, Project#portfolio=(portfolio), Project#portfolio.nil?</tt> |
| 102 | # * <tt>Project#project_manager, Project#project_manager=(project_manager), Project#project_manager.nil?,</tt> |
| 103 | # * <tt>Project#milestones.empty?, Project#milestones.size, Project#milestones, Project#milestones<<(milestone),</tt> |
| 104 | # <tt>Project#milestones.delete(milestone), Project#milestones.find(milestone_id), Project#milestones.find(:all, options),</tt> |
| 105 | # <tt>Project#milestones.build, Project#milestones.create</tt> |
| 106 | # * <tt>Project#categories.empty?, Project#categories.size, Project#categories, Project#categories<<(category1),</tt> |
| 107 | # <tt>Project#categories.delete(category1)</tt> |
| 108 | # |
| 109 | # === A word of warning |
| 110 | # |
| 111 | # Don't create associations that have the same name as instance methods of ActiveRecord::Base. Since the association |
| 112 | # adds a method with that name to its model, it will override the inherited method and break things. |
| 113 | # For instance, #attributes and #connection would be bad choices for association names. |
| 114 | # |
| 115 | # == Auto-generated methods |
| 116 | # |
| 117 | # === Singular associations (one-to-one) |
| 118 | # | | belongs_to | |
| 119 | # generated methods | belongs_to | :polymorphic | has_one |
| 120 | # ----------------------------------+------------+--------------+--------- |
| 121 | # #other | X | X | X |
| 122 | # #other=(other) | X | X | X |
| 123 | # #build_other(attributes={}) | X | | X |
| 124 | # #create_other(attributes={}) | X | | X |
| 125 | # #other.create!(attributes={}) | | | X |
| 126 | # #other.nil? | X | X | |
| 127 | # |
| 128 | # ===Collection associations (one-to-many / many-to-many) |
| 129 | # | | | has_many |
| 130 | # generated methods | habtm | has_many | :through |
| 131 | # ----------------------------------+-------+----------+---------- |
| 132 | # #others | X | X | X |
| 133 | # #others=(other,other,...) | X | X | X |
| 134 | # #other_ids | X | X | X |
| 135 | # #other_ids=(id,id,...) | X | X | X |
| 136 | # #others<< | X | X | X |
| 137 | # #others.push | X | X | X |
| 138 | # #others.concat | X | X | X |
| 139 | # #others.build(attributes={}) | X | X | X |
| 140 | # #others.create(attributes={}) | X | X | X |
| 141 | # #others.create!(attributes={}) | X | X | X |
| 142 | # #others.size | X | X | X |
| 143 | # #others.length | X | X | X |
| 144 | # #others.count | X | X | X |
| 145 | # #others.sum(args*,&block) | X | X | X |
| 146 | # #others.empty? | X | X | X |
| 147 | # #others.clear | X | X | X |
| 148 | # #others.delete(other,other,...) | X | X | X |
| 149 | # #others.delete_all | X | X | |
| 150 | # #others.destroy_all | X | X | X |
| 151 | # #others.find(*args) | X | X | X |
| 152 | # #others.find_first | X | | |
| 153 | # #others.uniq | X | X | X |
| 154 | # #others.reset | X | X | X |
| 155 | # |
| 156 | # == Cardinality and associations |
| 157 | # |
| 158 | # ActiveRecord associations can be used to describe one-to-one, one-to-many and many-to-many |
| 159 | # relationships between models. Each model uses an association to describe its role in |
| 160 | # the relation. The +belongs_to+ association is always used in the model that has |
| 161 | # the foreign key. |
| 162 | # |
| 163 | # === One-to-one |
| 164 | # |
| 165 | # Use +has_one+ in the base, and +belongs_to+ in the associated model. |
| 166 | # |
| 167 | # class Employee < ActiveRecord::Base |
| 168 | # has_one :office |
| 169 | # end |
| 170 | # class Office < ActiveRecord::Base |
| 171 | # belongs_to :employee # foreign key - employee_id |
| 172 | # end |
| 173 | # |
| 174 | # === One-to-many |
| 175 | # |
| 176 | # Use +has_many+ in the base, and +belongs_to+ in the associated model. |
| 177 | # |
| 178 | # class Manager < ActiveRecord::Base |
| 179 | # has_many :employees |
| 180 | # end |
| 181 | # class Employee < ActiveRecord::Base |
| 182 | # belongs_to :manager # foreign key - manager_id |
| 183 | # end |
| 184 | # |
| 185 | # === Many-to-many |
| 186 | # |
| 187 | # There are two ways to build a many-to-many relationship. |
| 188 | # |
| 189 | # The first way uses a +has_many+ association with the <tt>:through</tt> option and a join model, so |
| 190 | # there are two stages of associations. |
| 191 | # |
| 192 | # class Assignment < ActiveRecord::Base |
| 193 | # belongs_to :programmer # foreign key - programmer_id |
| 194 | # belongs_to :project # foreign key - project_id |
| 195 | # end |
| 196 | # class Programmer < ActiveRecord::Base |
| 197 | # has_many :assignments |
| 198 | # has_many :projects, :through => :assignments |
| 199 | # end |
| 200 | # class Project < ActiveRecord::Base |
| 201 | # has_many :assignments |
| 202 | # has_many :programmers, :through => :assignments |
| 203 | # end |
| 204 | # |
| 205 | # For the second way, use +has_and_belongs_to_many+ in both models. This requires a join table |
| 206 | # that has no corresponding model or primary key. |
| 207 | # |
| 208 | # class Programmer < ActiveRecord::Base |
| 209 | # has_and_belongs_to_many :projects # foreign keys in the join table |
| 210 | # end |
| 211 | # class Project < ActiveRecord::Base |
| 212 | # has_and_belongs_to_many :programmers # foreign keys in the join table |
| 213 | # end |
| 214 | # |
| 215 | # Choosing which way to build a many-to-many relationship is not always simple. |
| 216 | # If you need to work with the relationship model as its own entity, |
| 217 | # use <tt>has_many :through</tt>. Use +has_and_belongs_to_many+ when working with legacy schemas or when |
| 218 | # you never work directly with the relationship itself. |
| 219 | # |
| 220 | # == Is it a +belongs_to+ or +has_one+ association? |
| 221 | # |
| 222 | # Both express a 1-1 relationship. The difference is mostly where to place the foreign key, which goes on the table for the class |
| 223 | # declaring the +belongs_to+ relationship. Example: |
| 224 | # |
| 225 | # class User < ActiveRecord::Base |
| 226 | # # I reference an account. |
| 227 | # belongs_to :account |
| 228 | # end |
| 229 | # |
| 230 | # class Account < ActiveRecord::Base |
| 231 | # # One user references me. |
| 232 | # has_one :user |
| 233 | # end |
| 234 | # |
| 235 | # The tables for these classes could look something like: |
| 236 | # |
| 237 | # CREATE TABLE users ( |
| 238 | # id int(11) NOT NULL auto_increment, |
| 239 | # account_id int(11) default NULL, |
| 240 | # name varchar default NULL, |
| 241 | # PRIMARY KEY (id) |
| 242 | # ) |
| 243 | # |
| 244 | # CREATE TABLE accounts ( |
| 245 | # id int(11) NOT NULL auto_increment, |
| 246 | # name varchar default NULL, |
| 247 | # PRIMARY KEY (id) |
| 248 | # ) |
| 249 | # |
| 250 | # == Unsaved objects and associations |
| 251 | # |
| 252 | # You can manipulate objects and associations before they are saved to the database, but there is some special behavior you should be |
| 253 | # aware of, mostly involving the saving of associated objects. |
| 254 | # |
| 255 | # === One-to-one associations |
| 256 | # |
| 257 | # * Assigning an object to a +has_one+ association automatically saves that object and the object being replaced (if there is one), in |
| 258 | # order to update their primary keys - except if the parent object is unsaved (<tt>new_record? == true</tt>). |
| 259 | # * If either of these saves fail (due to one of the objects being invalid) the assignment statement returns +false+ and the assignment |
| 260 | # is cancelled. |
| 261 | # * If you wish to assign an object to a +has_one+ association without saving it, use the <tt>#association.build</tt> method (documented below). |
| 262 | # * Assigning an object to a +belongs_to+ association does not save the object, since the foreign key field belongs on the parent. It |
| 263 | # does not save the parent either. |
| 264 | # |
| 265 | # === Collections |
| 266 | # |
| 267 | # * Adding an object to a collection (+has_many+ or +has_and_belongs_to_many+) automatically saves that object, except if the parent object |
| 268 | # (the owner of the collection) is not yet stored in the database. |
| 269 | # * If saving any of the objects being added to a collection (via <tt>#push</tt> or similar) fails, then <tt>#push</tt> returns +false+. |
| 270 | # * You can add an object to a collection without automatically saving it by using the <tt>#collection.build</tt> method (documented below). |
| 271 | # * All unsaved (<tt>new_record? == true</tt>) members of the collection are automatically saved when the parent is saved. |
| 272 | # |
| 273 | # === Association callbacks |
| 274 | # |
| 275 | # Similar to the normal callbacks that hook into the lifecycle of an Active Record object, you can also define callbacks that get |
| 276 | # triggered when you add an object to or remove an object from an association collection. Example: |
| 277 | # |
| 278 | # class Project |
| 279 | # has_and_belongs_to_many :developers, :after_add => :evaluate_velocity |
| 280 | # |
| 281 | # def evaluate_velocity(developer) |
| 282 | # ... |
| 283 | # end |
| 284 | # end |
| 285 | # |
| 286 | # It's possible to stack callbacks by passing them as an array. Example: |
| 287 | # |
| 288 | # class Project |
| 289 | # has_and_belongs_to_many :developers, :after_add => [:evaluate_velocity, Proc.new { |p, d| p.shipping_date = Time.now}] |
| 290 | # end |
| 291 | # |
| 292 | # Possible callbacks are: +before_add+, +after_add+, +before_remove+ and +after_remove+. |
| 293 | # |
| 294 | # Should any of the +before_add+ callbacks throw an exception, the object does not get added to the collection. Same with |
| 295 | # the +before_remove+ callbacks; if an exception is thrown the object doesn't get removed. |
| 296 | # |
| 297 | # === Association extensions |
| 298 | # |
| 299 | # The proxy objects that control the access to associations can be extended through anonymous modules. This is especially |
| 300 | # beneficial for adding new finders, creators, and other factory-type methods that are only used as part of this association. |
| 301 | # Example: |
| 302 | # |
| 303 | # class Account < ActiveRecord::Base |
| 304 | # has_many :people do |
| 305 | # def find_or_create_by_name(name) |
| 306 | # first_name, last_name = name.split(" ", 2) |
| 307 | # find_or_create_by_first_name_and_last_name(first_name, last_name) |
| 308 | # end |
| 309 | # end |
| 310 | # end |
| 311 | # |
| 312 | # person = Account.find(:first).people.find_or_create_by_name("David Heinemeier Hansson") |
| 313 | # person.first_name # => "David" |
| 314 | # person.last_name # => "Heinemeier Hansson" |
| 315 | # |
| 316 | # If you need to share the same extensions between many associations, you can use a named extension module. Example: |
| 317 | # |
| 318 | # module FindOrCreateByNameExtension |
| 319 | # def find_or_create_by_name(name) |
| 320 | # first_name, last_name = name.split(" ", 2) |
| 321 | # find_or_create_by_first_name_and_last_name(first_name, last_name) |
| 322 | # end |
| 323 | # end |
| 324 | # |
| 325 | # class Account < ActiveRecord::Base |
| 326 | # has_many :people, :extend => FindOrCreateByNameExtension |
| 327 | # end |
| 328 | # |
| 329 | # class Company < ActiveRecord::Base |
| 330 | # has_many :people, :extend => FindOrCreateByNameExtension |
| 331 | # end |
| 332 | # |
| 333 | # If you need to use multiple named extension modules, you can specify an array of modules with the <tt>:extend</tt> option. |
| 334 | # In the case of name conflicts between methods in the modules, methods in modules later in the array supercede |
| 335 | # those earlier in the array. Example: |
| 336 | # |
| 337 | # class Account < ActiveRecord::Base |
| 338 | # has_many :people, :extend => [FindOrCreateByNameExtension, FindRecentExtension] |
| 339 | # end |
| 340 | # |
| 341 | # Some extensions can only be made to work with knowledge of the association proxy's internals. |
| 342 | # Extensions can access relevant state using accessors on the association proxy: |
| 343 | # |
| 344 | # * +proxy_owner+ - Returns the object the association is part of. |
| 345 | # * +proxy_reflection+ - Returns the reflection object that describes the association. |
| 346 | # * +proxy_target+ - Returns the associated object for +belongs_to+ and +has_one+, or the collection of associated objects for +has_many+ and +has_and_belongs_to_many+. |
| 347 | # |
| 348 | # === Association Join Models |
| 349 | # |
| 350 | # Has Many associations can be configured with the <tt>:through</tt> option to use an explicit join model to retrieve the data. This |
| 351 | # operates similarly to a +has_and_belongs_to_many+ association. The advantage is that you're able to add validations, |
| 352 | # callbacks, and extra attributes on the join model. Consider the following schema: |
| 353 | # |
| 354 | # class Author < ActiveRecord::Base |
| 355 | # has_many :authorships |
| 356 | # has_many :books, :through => :authorships |
| 357 | # end |
| 358 | # |
| 359 | # class Authorship < ActiveRecord::Base |
| 360 | # belongs_to :author |
| 361 | # belongs_to :book |
| 362 | # end |
| 363 | # |
| 364 | # @author = Author.find :first |
| 365 | # @author.authorships.collect { |a| a.book } # selects all books that the author's authorships belong to. |
| 366 | # @author.books # selects all books by using the Authorship join model |
| 367 | # |
| 368 | # You can also go through a +has_many+ association on the join model: |
| 369 | # |
| 370 | # class Firm < ActiveRecord::Base |
| 371 | # has_many :clients |
| 372 | # has_many :invoices, :through => :clients |
| 373 | # end |
| 374 | # |
| 375 | # class Client < ActiveRecord::Base |
| 376 | # belongs_to :firm |
| 377 | # has_many :invoices |
| 378 | # end |
| 379 | # |
| 380 | # class Invoice < ActiveRecord::Base |
| 381 | # belongs_to :client |
| 382 | # end |
| 383 | # |
| 384 | # @firm = Firm.find :first |
| 385 | # @firm.clients.collect { |c| c.invoices }.flatten # select all invoices for all clients of the firm |
| 386 | # @firm.invoices # selects all invoices by going through the Client join model. |
| 387 | # |
| 388 | # === Polymorphic Associations |
| 389 | # |
| 390 | # Polymorphic associations on models are not restricted on what types of models they can be associated with. Rather, they |
| 391 | # specify an interface that a +has_many+ association must adhere to. |
| 392 | # |
| 393 | # class Asset < ActiveRecord::Base |
| 394 | # belongs_to :attachable, :polymorphic => true |
| 395 | # end |
| 396 | # |
| 397 | # class Post < ActiveRecord::Base |
| 398 | # has_many :assets, :as => :attachable # The :as option specifies the polymorphic interface to use. |
| 399 | # end |
| 400 | # |
| 401 | # @asset.attachable = @post |
| 402 | # |
| 403 | # This works by using a type column in addition to a foreign key to specify the associated record. In the Asset example, you'd need |
| 404 | # an +attachable_id+ integer column and an +attachable_type+ string column. |
| 405 | # |
| 406 | # Using polymorphic associations in combination with single table inheritance (STI) is a little tricky. In order |
| 407 | # for the associations to work as expected, ensure that you store the base model for the STI models in the |
| 408 | # type column of the polymorphic association. To continue with the asset example above, suppose there are guest posts |
| 409 | # and member posts that use the posts table for STI. In this case, there must be a +type+ column in the posts table. |
| 410 | # |
| 411 | # class Asset < ActiveRecord::Base |
| 412 | # belongs_to :attachable, :polymorphic => true |
| 413 | # |
| 414 | # def attachable_type=(sType) |
| 415 | # super(sType.to_s.classify.constantize.base_class.to_s) |
| 416 | # end |
| 417 | # end |
| 418 | # |
| 419 | # class Post < ActiveRecord::Base |
| 420 | # # because we store "Post" in attachable_type now :dependent => :destroy will work |
| 421 | # has_many :assets, :as => :attachable, :dependent => :destroy |
| 422 | # end |
| 423 | # |
| 424 | # class GuestPost < Post |
| 425 | # end |
| 426 | # |
| 427 | # class MemberPost < Post |
| 428 | # end |
| 429 | # |
| 430 | # == Caching |
| 431 | # |
| 432 | # All of the methods are built on a simple caching principle that will keep the result of the last query around unless specifically |
| 433 | # instructed not to. The cache is even shared across methods to make it even cheaper to use the macro-added methods without |
| 434 | # worrying too much about performance at the first go. Example: |
| 435 | # |
| 436 | # project.milestones # fetches milestones from the database |
| 437 | # project.milestones.size # uses the milestone cache |
| 438 | # project.milestones.empty? # uses the milestone cache |
| 439 | # project.milestones(true).size # fetches milestones from the database |
| 440 | # project.milestones # uses the milestone cache |
| 441 | # |
| 442 | # == Eager loading of associations |
| 443 | # |
| 444 | # Eager loading is a way to find objects of a certain class and a number of named associations along with it in a single SQL call. This is |
| 445 | # one of the easiest ways of to prevent the dreaded 1+N problem in which fetching 100 posts that each need to display their author |
| 446 | # triggers 101 database queries. Through the use of eager loading, the 101 queries can be reduced to 1. Example: |
| 447 | # |
| 448 | # class Post < ActiveRecord::Base |
| 449 | # belongs_to :author |
| 450 | # has_many :comments |
| 451 | # end |
| 452 | # |
| 453 | # Consider the following loop using the class above: |
| 454 | # |
| 455 | # for post in Post.find(:all) |
| 456 | # puts "Post: " + post.title |
| 457 | # puts "Written by: " + post.author.name |
| 458 | # puts "Last comment on: " + post.comments.first.created_on |
| 459 | # end |
| 460 | # |
| 461 | # To iterate over these one hundred posts, we'll generate 201 database queries. Let's first just optimize it for retrieving the author: |
| 462 | # |
| 463 | # for post in Post.find(:all, :include => :author) |
| 464 | # |
| 465 | # This references the name of the +belongs_to+ association that also used the <tt>:author</tt> symbol, so the find will now weave in a join something |
| 466 | # like this: <tt>LEFT OUTER JOIN authors ON authors.id = posts.author_id</tt>. Doing so will cut down the number of queries from 201 to 101. |
| 467 | # |
| 468 | # We can improve upon the situation further by referencing both associations in the finder with: |
| 469 | # |
| 470 | # for post in Post.find(:all, :include => [ :author, :comments ]) |
| 471 | # |
| 472 | # That'll add another join along the lines of: <tt>LEFT OUTER JOIN comments ON comments.post_id = posts.id</tt>. And we'll be down to 1 query. |
| 473 | # |
| 474 | # To include a deep hierarchy of associations, use a hash: |
| 475 | # |
| 476 | # for post in Post.find(:all, :include => [ :author, { :comments => { :author => :gravatar } } ]) |
| 477 | # |
| 478 | # That'll grab not only all the comments but all their authors and gravatar pictures. You can mix and match |
| 479 | # symbols, arrays and hashes in any combination to describe the associations you want to load. |
| 480 | # |
| 481 | # All of this power shouldn't fool you into thinking that you can pull out huge amounts of data with no performance penalty just because you've reduced |
| 482 | # the number of queries. The database still needs to send all the data to Active Record and it still needs to be processed. So it's no |
| 483 | # catch-all for performance problems, but it's a great way to cut down on the number of queries in a situation as the one described above. |
| 484 | # |
| 485 | # Since the eager loading pulls from multiple tables, you'll have to disambiguate any column references in both conditions and orders. So |
| 486 | # <tt>:order => "posts.id DESC"</tt> will work while <tt>:order => "id DESC"</tt> will not. Because eager loading generates the +SELECT+ statement too, the |
| 487 | # <tt>:select</tt> option is ignored. |
| 488 | # |
| 489 | # You can use eager loading on multiple associations from the same table, but you cannot use those associations in orders and conditions |
| 490 | # as there is currently not any way to disambiguate them. Eager loading will not pull additional attributes on join tables, so "rich |
| 491 | # associations" with +has_and_belongs_to_many+ are not a good fit for eager loading. |
| 492 | # |
| 493 | # When eager loaded, conditions are interpolated in the context of the model class, not the model instance. Conditions are lazily interpolated |
| 494 | # before the actual model exists. |
| 495 | # |
| 496 | # Eager loading is not supported with polymorphic associations up to (and including) |
| 497 | # version 2.0.2. Given |
| 498 | # |
| 499 | # class Address < ActiveRecord::Base |
| 500 | # belongs_to :addressable, :polymorphic => true |
| 501 | # end |
| 502 | # |
| 503 | # a call that tries to eager load the addressable model |
| 504 | # |
| 505 | # Address.find(:all, :include => :addressable) # INVALID |
| 506 | # |
| 507 | # will raise <tt>ActiveRecord::EagerLoadPolymorphicError</tt>. The reason is that the parent model's type |
| 508 | # is a column value so its corresponding table name cannot be put in the FROM/JOIN clauses of that early query. |
| 509 | # |
| 510 | # In versions greater than 2.0.2 eager loading in polymorphic associations is supported |
| 511 | # thanks to a change in the overall preloading strategy. |
| 512 | # |
| 513 | # It does work the other way around though: if the <tt>User</tt> model is <tt>addressable</tt> you can eager load |
| 514 | # their addresses with <tt>:include</tt> just fine, every piece needed to construct the query is known beforehand. |
| 515 | # |
| 516 | # == Table Aliasing |
| 517 | # |
| 518 | # ActiveRecord uses table aliasing in the case that a table is referenced multiple times in a join. If a table is referenced only once, |
| 519 | # the standard table name is used. The second time, the table is aliased as <tt>#{reflection_name}_#{parent_table_name}</tt>. Indexes are appended |
| 520 | # for any more successive uses of the table name. |
| 521 | # |
| 522 | # Post.find :all, :include => :comments |
| 523 | # # => SELECT ... FROM posts LEFT OUTER JOIN comments ON ... |
| 524 | # Post.find :all, :include => :special_comments # STI |
| 525 | # # => SELECT ... FROM posts LEFT OUTER JOIN comments ON ... AND comments.type = 'SpecialComment' |
| 526 | # Post.find :all, :include => [:comments, :special_comments] # special_comments is the reflection name, posts is the parent table name |
| 527 | # # => SELECT ... FROM posts LEFT OUTER JOIN comments ON ... LEFT OUTER JOIN comments special_comments_posts |
| 528 | # |
| 529 | # Acts as tree example: |
| 530 | # |
| 531 | # TreeMixin.find :all, :include => :children |
| 532 | # # => SELECT ... FROM mixins LEFT OUTER JOIN mixins childrens_mixins ... |
| 533 | # TreeMixin.find :all, :include => {:children => :parent} # using cascading eager includes |
| 534 | # # => SELECT ... FROM mixins LEFT OUTER JOIN mixins childrens_mixins ... |
| 535 | # LEFT OUTER JOIN parents_mixins ... |
| 536 | # TreeMixin.find :all, :include => {:children => {:parent => :children}} |
| 537 | # # => SELECT ... FROM mixins LEFT OUTER JOIN mixins childrens_mixins ... |
| 538 | # LEFT OUTER JOIN parents_mixins ... |
| 539 | # LEFT OUTER JOIN mixins childrens_mixins_2 |
| 540 | # |
| 541 | # Has and Belongs to Many join tables use the same idea, but add a <tt>_join</tt> suffix: |
| 542 | # |
| 543 | # Post.find :all, :include => :categories |
| 544 | # # => SELECT ... FROM posts LEFT OUTER JOIN categories_posts ... LEFT OUTER JOIN categories ... |
| 545 | # Post.find :all, :include => {:categories => :posts} |
| 546 | # # => SELECT ... FROM posts LEFT OUTER JOIN categories_posts ... LEFT OUTER JOIN categories ... |
| 547 | # LEFT OUTER JOIN categories_posts posts_categories_join LEFT OUTER JOIN posts posts_categories |
| 548 | # Post.find :all, :include => {:categories => {:posts => :categories}} |
| 549 | # # => SELECT ... FROM posts LEFT OUTER JOIN categories_posts ... LEFT OUTER JOIN categories ... |
| 550 | # LEFT OUTER JOIN categories_posts posts_categories_join LEFT OUTER JOIN posts posts_categories |
| 551 | # LEFT OUTER JOIN categories_posts categories_posts_join LEFT OUTER JOIN categories categories_posts |
| 552 | # |
| 553 | # If you wish to specify your own custom joins using a <tt>:joins</tt> option, those table names will take precedence over the eager associations: |
| 554 | # |
| 555 | # Post.find :all, :include => :comments, :joins => "inner join comments ..." |
| 556 | # # => SELECT ... FROM posts LEFT OUTER JOIN comments_posts ON ... INNER JOIN comments ... |
| 557 | # Post.find :all, :include => [:comments, :special_comments], :joins => "inner join comments ..." |
| 558 | # # => SELECT ... FROM posts LEFT OUTER JOIN comments comments_posts ON ... |
| 559 | # LEFT OUTER JOIN comments special_comments_posts ... |
| 560 | # INNER JOIN comments ... |
| 561 | # |
| 562 | # Table aliases are automatically truncated according to the maximum length of table identifiers according to the specific database. |
| 563 | # |
| 564 | # == Modules |
| 565 | # |
| 566 | # By default, associations will look for objects within the current module scope. Consider: |
| 567 | # |
| 568 | # module MyApplication |
| 569 | # module Business |
| 570 | # class Firm < ActiveRecord::Base |
| 571 | # has_many :clients |
| 572 | # end |
| 573 | # |
| 574 | # class Company < ActiveRecord::Base; end |
| 575 | # end |
| 576 | # end |
| 577 | # |
| 578 | # When <tt>Firm#clients</tt> is called, it will in turn call <tt>MyApplication::Business::Company.find(firm.id)</tt>. If you want to associate |
| 579 | # with a class in another module scope, this can be done by specifying the complete class name. Example: |
| 580 | # |
| 581 | # module MyApplication |
| 582 | # module Business |
| 583 | # class Firm < ActiveRecord::Base; end |
| 584 | # end |
| 585 | # |
| 586 | # module Billing |
| 587 | # class Account < ActiveRecord::Base |
| 588 | # belongs_to :firm, :class_name => "MyApplication::Business::Firm" |
| 589 | # end |
| 590 | # end |
| 591 | # end |
| 592 | # |
| 593 | # == Type safety with <tt>ActiveRecord::AssociationTypeMismatch</tt> |
| 594 | # |
| 595 | # If you attempt to assign an object to an association that doesn't match the inferred or specified <tt>:class_name</tt>, you'll |
| 596 | # get an <tt>ActiveRecord::AssociationTypeMismatch</tt>. |
| 597 | # |
| 598 | # == Options |
| 599 | # |
| 600 | # All of the association macros can be specialized through options. This makes cases more complex than the simple and guessable ones |
| 601 | # possible. |
| 602 | module ClassMethods |
| 603 | # Adds the following methods for retrieval and query of collections of associated objects: |
| 604 | # +collection+ is replaced with the symbol passed as the first argument, so |
| 605 | # <tt>has_many :clients</tt> would add among others <tt>clients.empty?</tt>. |
| 606 | # * <tt>collection(force_reload = false)</tt> - returns an array of all the associated objects. |
| 607 | # An empty array is returned if none are found. |
| 608 | # * <tt>collection<<(object, ...)</tt> - adds one or more objects to the collection by setting their foreign keys to the collection's primary key. |
| 609 | # * <tt>collection.delete(object, ...)</tt> - removes one or more objects from the collection by setting their foreign keys to NULL. |
| 610 | # This will also destroy the objects if they're declared as +belongs_to+ and dependent on this model. |
| 611 | # * <tt>collection=objects</tt> - replaces the collections content by deleting and adding objects as appropriate. |
| 612 | # * <tt>collection_singular_ids</tt> - returns an array of the associated objects' ids |
| 613 | # * <tt>collection_singular_ids=ids</tt> - replace the collection with the objects identified by the primary keys in +ids+ |
| 614 | # * <tt>collection.clear</tt> - removes every object from the collection. This destroys the associated objects if they |
| 615 | # are associated with <tt>:dependent => :destroy</tt>, deletes them directly from the database if <tt>:dependent => :delete_all</tt>, |
| 616 | # otherwise sets their foreign keys to NULL. |
| 617 | # * <tt>collection.empty?</tt> - returns +true+ if there are no associated objects. |
| 618 | # * <tt>collection.size</tt> - returns the number of associated objects. |
| 619 | # * <tt>collection.find</tt> - finds an associated object according to the same rules as Base.find. |
| 620 | # * <tt>collection.build(attributes = {}, ...)</tt> - returns one or more new objects of the collection type that have been instantiated |
| 621 | # with +attributes+ and linked to this object through a foreign key, but have not yet been saved. *Note:* This only works if an |
| 622 | # associated object already exists, not if it's +nil+! |
| 623 | # * <tt>collection.create(attributes = {})</tt> - returns a new object of the collection type that has been instantiated |
| 624 | # with +attributes+, linked to this object through a foreign key, and that has already been saved (if it passed the validation). |
| 625 | # *Note:* This only works if an associated object already exists, not if it's +nil+! |
| 626 | # |
| 627 | # Example: A +Firm+ class declares <tt>has_many :clients</tt>, which will add: |
| 628 | # * <tt>Firm#clients</tt> (similar to <tt>Clients.find :all, :conditions => "firm_id = #{id}"</tt>) |
| 629 | # * <tt>Firm#clients<<</tt> |
| 630 | # * <tt>Firm#clients.delete</tt> |
| 631 | # * <tt>Firm#clients=</tt> |
| 632 | # * <tt>Firm#client_ids</tt> |
| 633 | # * <tt>Firm#client_ids=</tt> |
| 634 | # * <tt>Firm#clients.clear</tt> |
| 635 | # * <tt>Firm#clients.empty?</tt> (similar to <tt>firm.clients.size == 0</tt>) |
| 636 | # * <tt>Firm#clients.size</tt> (similar to <tt>Client.count "firm_id = #{id}"</tt>) |
| 637 | # * <tt>Firm#clients.find</tt> (similar to <tt>Client.find(id, :conditions => "firm_id = #{id}")</tt>) |
| 638 | # * <tt>Firm#clients.build</tt> (similar to <tt>Client.new("firm_id" => id)</tt>) |
| 639 | # * <tt>Firm#clients.create</tt> (similar to <tt>c = Client.new("firm_id" => id); c.save; c</tt>) |
| 640 | # The declaration can also include an options hash to specialize the behavior of the association. |
| 641 | # |
| 642 | # Options are: |
| 643 | # * <tt>:class_name</tt> - specify the class name of the association. Use it only if that name can't be inferred |
| 644 | # from the association name. So <tt>has_many :products</tt> will by default be linked to the +Product+ class, but |
| 645 | # if the real class name is +SpecialProduct+, you'll have to specify it with this option. |
| 646 | # * <tt>:conditions</tt> - specify the conditions that the associated objects must meet in order to be included as a +WHERE+ |
| 647 | # SQL fragment, such as <tt>price > 5 AND name LIKE 'B%'</tt>. Record creations from the association are scoped if a hash |
| 648 | # is used. <tt>has_many :posts, :conditions => {:published => true}</tt> will create published posts with <tt>@blog.posts.create</tt> |
| 649 | # or <tt>@blog.posts.build</tt>. |
| 650 | # * <tt>:order</tt> - specify the order in which the associated objects are returned as an <tt>ORDER BY</tt> SQL fragment, |
| 651 | # such as <tt>last_name, first_name DESC</tt> |
| 652 | # * <tt>:foreign_key</tt> - specify the foreign key used for the association. By default this is guessed to be the name |
| 653 | # of this class in lower-case and +_id+ suffixed. So a +Person+ class that makes a +has_many+ association will use +person_id+ |
| 654 | # as the default +foreign_key+. |
| 655 | # * <tt>:dependent</tt> - if set to <tt>:destroy</tt> all the associated objects are destroyed |
| 656 | # alongside this object by calling their destroy method. If set to <tt>:delete_all</tt> all associated |
| 657 | # objects are deleted *without* calling their destroy method. If set to <tt>:nullify</tt> all associated |
| 658 | # objects' foreign keys are set to +NULL+ *without* calling their save callbacks. *Warning:* This option is ignored when also using |
| 659 | # the <tt>through</tt> option. |
| 660 | # * <tt>:finder_sql</tt> - specify a complete SQL statement to fetch the association. This is a good way to go for complex |
| 661 | # associations that depend on multiple tables. Note: When this option is used, +find_in_collection+ is _not_ added. |
| 662 | # * <tt>:counter_sql</tt> - specify a complete SQL statement to fetch the size of the association. If <tt>:finder_sql</tt> is |
| 663 | # specified but not <tt>:counter_sql</tt>, <tt>:counter_sql</tt> will be generated by replacing <tt>SELECT ... FROM</tt> with <tt>SELECT COUNT(*) FROM</tt>. |
| 664 | # * <tt>:extend</tt> - specify a named module for extending the proxy. See "Association extensions". |
| 665 | # * <tt>:include</tt> - specify second-order associations that should be eager loaded when the collection is loaded. |
| 666 | # * <tt>:group</tt>: An attribute name by which the result should be grouped. Uses the <tt>GROUP BY</tt> SQL-clause. |
| 667 | # * <tt>:limit</tt>: An integer determining the limit on the number of rows that should be returned. |
| 668 | # * <tt>:offset</tt>: An integer determining the offset from where the rows should be fetched. So at 5, it would skip the first 4 rows. |
| 669 | # * <tt>:select</tt>: By default, this is <tt>*</tt> as in <tt>SELECT * FROM</tt>, but can be changed if you, for example, want to do a join |
| 670 | # but not include the joined columns. |
| 671 | # * <tt>:as</tt>: Specifies a polymorphic interface (See <tt>#belongs_to</tt>). |
| 672 | # * <tt>:through</tt>: Specifies a Join Model through which to perform the query. Options for <tt>:class_name</tt> and <tt>:foreign_key</tt> |
| 673 | # are ignored, as the association uses the source reflection. You can only use a <tt>:through</tt> query through a <tt>belongs_to</tt> |
| 674 | # or <tt>has_many</tt> association on the join model. |
| 675 | # * <tt>:source</tt>: Specifies the source association name used by <tt>has_many :through</tt> queries. Only use it if the name cannot be |
| 676 | # inferred from the association. <tt>has_many :subscribers, :through => :subscriptions</tt> will look for either <tt>:subscribers</tt> or |
| 677 |