has_many
- 1.0.0 (0)
- 1.1.6 (38)
- 1.2.6 (11)
- 2.0.3 (-14)
- 2.1.0 (14)
- 2.2.1 (13)
- 2.3.8 (8)
- 3.0.0 (32)
- 3.0.9 (-9)
- 3.1.0 (19)
- 3.2.1 (4)
- 3.2.8 (0)
- 3.2.13 (0)
- 4.0.2 (-29)
- 4.1.8 (1)
- 4.2.1 (31)
- 4.2.7 (0)
- 4.2.9 (0)
- 5.0.0.1 (13)
- 5.1.7 (16)
- 5.2.3 (1)
- 6.0.0 (3)
- 6.1.3.1 (17)
- 6.1.7.7 (0)
- 7.0.0 (12)
- 7.1.3.2 (17)
- 7.1.3.4 (0)
- What's this?
has_many(name, scope = nil, options = {}, &extension)
public
Specifies a one-to-many association. The following methods for retrieval and query of collections of associated objects will be added:
collection is a placeholder for the symbol passed as the name argument, so has_many :clients would add among others clients.empty?.
- collection(force_reload = false)
-
Returns an array of all the associated objects. An empty array is returned if none are found.
- collection<<(object, …)
-
Adds one or more objects to the collection by setting their foreign keys to the collection’s primary key. Note that this operation instantly fires update SQL without waiting for the save or update call on the parent object, unless the parent object is a new record. This will also run validations and callbacks of associated object(s).
- collection.delete(object, …)
-
Removes one or more objects from the collection by setting their foreign keys to NULL. Objects will be in addition destroyed if they’re associated with dependent: :destroy, and deleted if they’re associated with dependent: :delete_all.
If the :through option is used, then the join records are deleted (rather than nullified) by default, but you can specify dependent: :destroy or dependent: :nullify to override this.
- collection.destroy(object, …)
-
Removes one or more objects from the collection by running destroy on each record, regardless of any dependent option, ensuring callbacks are run.
If the :through option is used, then the join records are destroyed instead, not the objects themselves.
- collection=objects
-
Replaces the collections content by deleting and adding objects as appropriate. If the :through option is true callbacks in the join models are triggered except destroy callbacks, since deletion is direct by default. You can specify dependent: :destroy or dependent: :nullify to override this.
- collection_singular_ids
-
Returns an array of the associated objects’ ids
- collection_singular_ids=ids
-
Replace the collection with the objects identified by the primary keys in ids. This method loads the models and calls collection=. See above.
- collection.clear
-
Removes every object from the collection. This destroys the associated objects if they are associated with dependent: :destroy, deletes them directly from the database if dependent: :delete_all, otherwise sets their foreign keys to NULL. If the :through option is true no destroy callbacks are invoked on the join models. Join models are directly deleted.
- collection.empty?
-
Returns true if there are no associated objects.
- collection.size
-
Returns the number of associated objects.
- collection.find(…)
-
Finds an associated object according to the same rules as ActiveRecord::FinderMethods#find.
- collection.exists?(…)
-
Checks whether an associated object with the given conditions exists. Uses the same rules as ActiveRecord::FinderMethods#exists?.
- collection.build(attributes = {}, …)
-
Returns one or more new objects of the collection type that have been instantiated with attributes and linked to this object through a foreign key, but have not yet been saved.
- collection.create(attributes = {})
-
Returns a new object of the collection type that has been instantiated with attributes, linked to this object through a foreign key, and that has already been saved (if it passed the validation). Note: This only works if the base model already exists in the DB, not if it is a new (unsaved) record!
- collection.create!(attributes = {})
-
Does the same as collection.create, but raises ActiveRecord::RecordInvalid if the record is invalid.
Example
A Firm class declares has_many :clients, which will add:
-
Firm#clients (similar to Client.where(firm_id: id))
-
Firm#clients<<
-
Firm#clients.delete
-
Firm#clients.destroy
-
Firm#clients=
-
Firm#client_ids
-
Firm#client_ids=
-
Firm#clients.clear
-
Firm#clients.empty? (similar to firm.clients.size == 0)
-
Firm#clients.size (similar to Client.count "firm_id = #{id}")
-
Firm#clients.find (similar to Client.where(firm_id: id).find(id))
-
Firm#clients.exists?(name: 'ACME') (similar to Client.exists?(name: 'ACME', firm_id: firm.id))
-
Firm#clients.build (similar to Client.new("firm_id" => id))
-
Firm#clients.create (similar to c = Client.new("firm_id" => id); c.save; c)
-
Firm#clients.create! (similar to c = Client.new("firm_id" => id); c.save!)
The declaration can also include an options hash to specialize the behavior of the association.
Scopes
You can pass a second argument scope as a callable (i.e. proc or lambda) to retrieve a specific set of records or customize the generated query when you access the associated collection.
Scope examples:
has_many :comments, -> { where(author_id: 1) } has_many :employees, -> { joins(:address) } has_many :posts, ->(post) { where("max_post_length > ?", post.length) }
Extensions
The extension argument allows you to pass a block into a has_many association. This is useful for adding new finders, creators and other factory-type methods to be used as part of the association.
Extension examples:
has_many :employees do def find_or_create_by_name(name) first_name, last_name = name.split(" ", 2) find_or_create_by(first_name: first_name, last_name: last_name) end end
Options
- :class_name
-
Specify the class name of the association. Use it only if that name can’t be inferred from the association name. So has_many :products will by default be linked to the Product class, but if the real class name is SpecialProduct, you’ll have to specify it with this option.
- :foreign_key
-
Specify the foreign key used for the association. By default this is guessed to be the name of this class in lower-case and “_id” suffixed. So a Person class that makes a #has_many association will use “person_id” as the default :foreign_key.
- :foreign_type
-
Specify the column used to store the associated object’s type, if this is a polymorphic association. By default this is guessed to be the name of the polymorphic association specified on “as” option with a “_type” suffix. So a class that defines a has_many :tags, as: :taggable association will use “taggable_type” as the default :foreign_type.
- :primary_key
-
Specify the name of the column to use as the primary key for the association. By default this is id.
- :dependent
-
Controls what happens to the associated objects when their owner is destroyed. Note that these are implemented as callbacks, and Rails executes callbacks in order. Therefore, other similar callbacks may affect the :dependent behavior, and the :dependent behavior may affect other callbacks.
-
:destroy causes all the associated objects to also be destroyed.
-
:delete_all causes all the associated objects to be deleted directly from the database (so callbacks will not be executed).
-
:nullify causes the foreign keys to be set to NULL. Callbacks are not executed.
-
:restrict_with_exception causes an exception to be raised if there are any associated records.
-
:restrict_with_error causes an error to be added to the owner if there are any associated objects.
If using with the :through option, the association on the join model must be a #belongs_to, and the records which get deleted are the join records, rather than the associated records.
-
- :counter_cache
-
This option can be used to configure a custom named :counter_cache. You only need this option, when you customized the name of your :counter_cache on the #belongs_to association.
- :as
-
Specifies a polymorphic interface (See #belongs_to).
- :through
-
Specifies an association through which to perform the query. This can be any other type of association, including other :through associations. Options for :class_name, :primary_key and :foreign_key are ignored, as the association uses the source reflection.
If the association on the join model is a #belongs_to, the collection can be modified and the records on the :through model will be automatically created and removed as appropriate. Otherwise, the collection is read-only, so you should manipulate the :through association directly.
If you are going to modify the association (rather than just read from it), then it is a good idea to set the :inverse_of option on the source association on the join model. This allows associated records to be built which will automatically create the appropriate join model records when they are saved. (See the ‘Association Join Models’ section above.)
- :source
-
Specifies the source association name used by #has_many :through queries. Only use it if the name cannot be inferred from the association. has_many :subscribers, through: :subscriptions will look for either :subscribers or :subscriber on Subscription, unless a :source is given.
- :source_type
-
Specifies type of the source association used by #has_many :through queries where the source association is a polymorphic #belongs_to.
- :validate
-
When set to true, validates new objects added to association when saving the parent object. true by default. If you want to ensure associated objects are revalidated on every update, use validates_associated.
- :autosave
-
If true, always save the associated objects or destroy them if marked for destruction, when saving the parent object. If false, never save or destroy the associated objects. By default, only save associated objects that are new records. This option is implemented as a before_save callback. Because callbacks are run in the order they are defined, associated objects may need to be explicitly saved in any user-defined before_save callbacks.
Note that NestedAttributes::ClassMethods#accepts_nested_attributes_for sets :autosave to true.
- :inverse_of
-
Specifies the name of the #belongs_to association on the associated object that is the inverse of this #has_many association. Does not work in combination with :through or :as options. See ActiveRecord::Associations::ClassMethods’s overview on Bi-directional associations for more detail.
- :extend
-
Specifies a module or array of modules that will be extended into the association object returned. Useful for defining methods on associations, especially when they should be shared between multiple association objects.
Option examples:
has_many :comments, -> { order("posted_on") } has_many :comments, -> { includes(:author) } has_many :people, -> { where(deleted: false).order("name") }, class_name: "Person" has_many :tracks, -> { order("position") }, dependent: :destroy has_many :comments, dependent: :nullify has_many :tags, as: :taggable has_many :reports, -> { readonly } has_many :subscribers, through: :subscriptions, source: :user
User a block to extend your associations
You can use blocks to extend your associations with extra methods.
code sample
has_many :children, :dependent => :destroy do def at(time) proxy_owner.children.find_with_deleted :all, :conditions => [ "created_at <= :time AND (deleted_at > :time OR deleted_at IS NULL)", { :time => time } ] end end Model.children.each # do stuff Model.children.at( 1.week.ago ).each # do old stuff
you must use ‘proxy_owner’ to link back to your model.
Gotcha when defining :finder_sql or :counter_sql
When setting custom SQL statements in the :finder_sql or :counter_sql queries, if you need to inject attributes from the current object, such as the ID, make sure to disable string interpolation of the statement by using single quotes or %q().
Example:
has_many :relationships, :class_name => 'Relationship', :finder_sql => %q( SELECT DISTINCT relationships.* FROM relationships WHERE contact_id = #{id} )
Surrounding this SQL with double-quotes or %Q() will expand #{id} too early, resulting in a warning about Object#id being deprecated and general brokenness.
Undocumented :inverse_of option
Support for the :inverse_of option was backported to 2.3.6+.
Here’s the description from the original commit: http://github.com/rails/rails/commit/ccea98389abbf150b886c9f964b1def47f00f237
You can now add an :inverse_of option to has_one, has_many and belongs_to associations. This is best described with an example:
class Man < ActiveRecord::Base has_one :face, :inverse_of => :man end class Face < ActiveRecord::Base belongs_to :man, :inverse_of => :face end m = Man.first f = m.face
Without :inverse_of m and f.man would be different instances of the same object (f.man being pulled from the database again). With these new :inverse_of options m and f.man are the same in memory instance.
Currently :inverse_of supports has_one and has_many (but not the :through variants) associations. It also supplies inverse support for belongs_to associations where the inverse is a has_one and it’s not a polymorphic.
Named scope better than conditions
In modern versions of Rails, in most cases a named_scope is a better alternative to using :conditions on your has_many relations. Compare:
class User has_many :published_posts, :conditions => {:published => true} end user.published_posts
with:
class Post named_scope :published, :conditions => {:published => true} end class User has_many :posts end user.posts.published
It’s better because the Post’s logic (“am I published?”) should not be coupled within User class. This makes it easier to refactor: e.g. if you wanted to refactor the boolean :published field into a :status field with more available values, you would not have to modify User class. Having to modify User when you refactor some implementation detail of Post class is clearly a code smell.
This also applies to :order, :group, :having and similar options.
Explanation about :dependent option
It may seem that :dependent option is only used when the object that has the collection is destroyed, but it is also used every time a associated object is deleted, so if you use
object.collection.delete(associated_object)
your object will be deleted, destroyed or nullified, depending on the value of :dependent option.
With has_many :through associations this option is ignored at least in versions up to 2.1.0, so even if you set :dependent option to :destroy, your join objects will be deleted, not firing any callbacks you have set on destroy events.
If you need to act when your join model is deleted you can use a sweeper or an observer and the association callbacks like this:
# product.rb class Product has_many :categorizations has_many :categories, :through => :categorizations, :before_remove => :fire_before_remove_in_categorizations private def fire_before_remove_in_categorizations(category) categorization = self.categorizations.find_by_category_id(category.id) categorization.class.changed categorization.class.notify_observers(:before_remove, categorization) end end # categorization_sweeper.rb # do not forget to load this sweeper during initialization class CategorizationSweeper < ActionController::Caching::Sweeper observe Categorization def before_remove(categorization) # expire_cache, expire_fragment, whatever end end
One thing you should be aware of it is that you are using before_remove, so you have to be careful because your record may be not be removed (another callback raising an exception or the database not deleting the record) so you can not be sure your object will be delete. Expiring caches is safe because even if your record is not destroyed your cache will be regerated correctly.
You can not use after_remove, because at that point the join model do not exists anymore, so you can not fire its callbacks. But you have the model id and the associated model id, so if you do not need expiring caches maybe you can use this approach (expiring caches can be only done in a sweeper or in a controller, but with after_remove you are bound to your model).
Undocumented callbacks
Not sure why this isn’t documented… there are callbacks for before/after_add and before/after_remove. Example
has_many :things, :after_add => :set_things, :after_remove => :remove_things def set_things(thing) ... end def remove_things(thing) ... end
Polymorphic has_many within inherited class gotcha
Given I have following classes
class User < ActiveRecord::Base end class ::User::Agent < ::User has_many :leads, :as => :creator end
I would expect, that running
User::Agent.first.leads
will result in following query
SELECT "leads".* FROM "leads" WHERE ("leads".creator_id = 6 AND "leads".creator_type = 'User::Agent')
however it results in
SELECT "leads".* FROM "leads" WHERE ("leads".creator_id = 6 AND "leads".creator_type = 'User')
Possible solutions:
-
Make User class use STI - polymorphic relations will then retrieve correct class from :type field (however in my situation it was not an option)
-
If You do never instantiate User class itself, mark it as abstract
class User < ActiveRecord::Base self.abstract_class = true end
-
If You do instantiate User class, as last resort You can overwrite base_class for User::Agent
class ::User::Agent < ::User has_many :leads, :as => :creator def self.base_class self end end
-
If none of above is an option and You do not care that You will lose some of relation’s features, You can always
class User::Agent < ::User has_many :leads, :as => :creator, :finder_sql => %q(SELECT "leads".* FROM "leads" WHERE ("leads".creator_id = #{id} AND "leads".creator_type = 'User::Agent')) end
collection update
in the FirmsController
@firm.people.update(params[:people].keys,params.values)
in the View
<% form_for(@firm) do |f| %>
<%= f.error_messages %> <%= f.text_field :name %> <%@firm.people.each do |person|%> <%fields_for "people[]", person do |pf|%> <%= pf.text_field :name %> <%end%> <%= f.submit "Save" %>
<%end%>
You can't have many :through with habtm
Imagine the following
a has_many b b has_and_belongs_to_many c a has_many c :through => b
a.b works fine
b.c works fine
a.c throws an error!
has_many :through where the through association is a habtm is not supported in Rails. The error is:
ActiveRecord::HasManyThroughSourceAssociationMacroError: Invalid source reflection macro :has_and_belongs_to_many for has_many :stories, :through => :subcategories. Use :source to specify the source reflection
Specifying the source reflection still won’t help you though, because this kind of has_many :through isn’t supported at all.