Class: ActiveRecord::Associations::HasManyAssociation
Relationships & Source Files | |
Extension / Inclusion / Inheritance Descendants | |
Subclasses:
|
|
Super Chains via Extension / Inclusion / Inheritance | |
Class Chain:
self,
CollectionAssociation ,
Association
|
|
Instance Chain:
|
|
Inherits: |
ActiveRecord::Associations::CollectionAssociation
|
Defined in: | activerecord/lib/active_record/associations/has_many_association.rb |
Overview
Active Record Has Many Association
This is the proxy that handles a has many association.
If the association has a :through
option further specialization is provided by its child HasManyThroughAssociation
.
Class Method Summary
Association
- Inherited
Instance Attribute Summary
ForeignAssociation
- Included
CollectionAssociation
- Inherited
#collection?, | |
#empty? | Returns true if the collection is empty. |
#find_from_target?, #nested_attributes_target, #null_scope?, #target= |
Association
- Inherited
#collection? | Whether the association represent a single record or a collection of records. |
#disable_joins, | |
#loaded? | Has the target been already loaded? |
#options, #owner, #reflection, | |
#stale_target? | The target is stale if the target no longer points to the record(s) that the relevant foreign_key(s) refers to. |
#target, | |
#target= | Sets the target of this association to |
#find_target?, | |
#foreign_key_present? | Returns true if there is a foreign key present on the owner which references the target. |
#violates_strict_loading? |
Instance Method Summary
- #handle_dependency
- #insert_record(record, validate = true, raise = false)
- #_create_record(attributes) private
- #concat_records(records) private
-
#count_records
private
Returns the number of records in this collection.
- #delete_count(method, scope) private
- #delete_or_nullify_all_records(method) private
-
#delete_records(records, method)
private
Deletes the records according to the
:dependent
option. - #difference(a, b) private
- #intersection(a, b) private
- #update_counter(difference, reflection = reflection()) private
- #update_counter_if_success(saved_successfully, difference) private
- #update_counter_in_memory(difference, reflection = reflection()) private
ForeignAssociation
- Included
#nullified_owner_attributes, | |
#set_owner_attributes | Sets the owner attributes on the given record. |
CollectionAssociation
- Inherited
#add_to_target, #build, | |
#concat | Add |
#delete | Removes |
#delete_all | Removes all records from the association without calling callbacks on the associated records. |
#destroy | Deletes the |
#destroy_all | Destroy all the records from this association. |
#find, | |
#ids_reader | Implements the ids reader method, e.g. |
#ids_writer | Implements the ids writer method, e.g. |
#include?, #load_target, | |
#reader | Implements the reader method, e.g. |
#replace | Replace this collection with |
#reset, #scope, | |
#size | Returns the size of the collection by executing a SELECT COUNT(*) query if the collection hasn’t been loaded, and calling |
#writer | Implements the writer method, e.g. |
#_create_record, #callback, #callbacks_for, #concat_records, #delete_or_destroy, | |
#delete_records | Delete the given records from the association, using one of the methods |
#find_by_scan | If the |
#include_in_memory?, | |
#insert_record | Do the relevant stuff to insert the given record into the association collection. |
#merge_target_lists | We have some records loaded from the database (persisted) and some that are in-memory (memory). |
#remove_records, #replace_common_records_in_memory, #replace_on_target, #replace_records, #transaction |
Association
- Inherited
#async_load_target, #create, #create!, #extensions, #initialize_attributes, #inversed_from, #inversed_from_queries, | |
#klass | Returns the class of the target. |
#load_target | Loads the target if needed and returns it. |
#loaded! | Asserts the target has been loaded setting the loaded flag to |
#marshal_dump | We can’t dump @reflection and @through_reflection since it contains the scope proc. |
#marshal_load, | |
#reload | Reloads the target and returns |
#remove_inverse_instance | Remove the inverse association, if possible. |
#reset | Resets the loaded flag to |
#reset_negative_cache, #reset_scope, #scope, | |
#set_inverse_instance | Set the inverse association, if possible. |
#set_inverse_instance_from_queries, #set_strict_loading, | |
#association_scope | The scope for this association. |
#build_record, #enqueue_destroy_association, | |
#ensure_klass_exists! | Reader and writer methods call this so that consistent errors are presented when the association target class does not exist. |
#find_target, | |
#foreign_key_for? | Returns true if record contains the foreign_key. |
#inversable?, #inverse_association_for, | |
#inverse_reflection_for | Can be redefined by subclasses, notably polymorphic belongs_to The record parameter is necessary to support polymorphic inverses as we must check for the association in the specific class of the record. |
#invertible_for? | Returns true if inverse association on the given record needs to be set. |
#matches_foreign_key?, | |
#raise_on_type_mismatch! | Raises |
#scope_for_create, | |
#skip_statement_cache? | Returns true if statement cache should be skipped on the association reader. |
#skip_strict_loading, | |
#stale_state | This should be implemented to return the values of the relevant key(s) on the owner, so that when stale_state is different from the value stored on the last find_target, the target is stale. |
#target_scope | Can be overridden (i.e. in |
Constructor Details
This class inherits a constructor from ActiveRecord::Associations::Association
Instance Method Details
#_create_record(attributes) (private)
[ GitHub ]# File 'activerecord/lib/active_record/associations/has_many_association.rb', line 143
def _create_record(attributes, *) if attributes.is_a?(Array) super else update_counter_if_success(super, 1) end end
#concat_records(records) (private)
[ GitHub ]# File 'activerecord/lib/active_record/associations/has_many_association.rb', line 139
def concat_records(records, *) update_counter_if_success(super, records.length) end
#count_records (private)
Returns the number of records in this collection.
If the association has a counter cache it gets that value. Otherwise it will attempt to do a count via SQL, bounded to :limit
if there’s one. Some configuration options like :group
make it impossible to do an SQL count, in those cases the array count will be used.
That does not depend on whether the collection has already been loaded or not. The size
method is the one that takes the loaded flag into account and delegates to count_records
if needed.
If the collection is empty the target is set to an empty array and the loaded flag is set to true as well.
# File 'activerecord/lib/active_record/associations/has_many_association.rb', line 80
def count_records count = if reflection.has_active_cached_counter? owner.read_attribute(reflection.counter_cache_column).to_i else scope.count(:all) end # If there's nothing in the database, @target should only contain new # records or be an empty array. This is a documented side-effect of # the method that may avoid an extra SELECT. if count == 0 target.select!(&:new_record?) loaded! end [association_scope.limit_value, count].compact.min end
#delete_count(method, scope) (private)
[ GitHub ]# File 'activerecord/lib/active_record/associations/has_many_association.rb', line 112
def delete_count(method, scope) if method == :delete_all scope.delete_all else scope.update_all(nullified_owner_attributes) end end
#delete_or_nullify_all_records(method) (private)
[ GitHub ]# File 'activerecord/lib/active_record/associations/has_many_association.rb', line 120
def delete_or_nullify_all_records(method) count = delete_count(method, scope) update_counter(-count) count end
#delete_records(records, method) (private)
Deletes the records according to the :dependent
option.
# File 'activerecord/lib/active_record/associations/has_many_association.rb', line 127
def delete_records(records, method) if method == :destroy records.each(&:destroy!) update_counter(-records.length) unless reflection.inverse_updates_counter_cache? else query_constraints = reflection.klass.composite_query_constraints_list values = records.map { |r| query_constraints.map { |col| r._read_attribute(col) } } scope = self.scope.where(query_constraints => values) update_counter(-delete_count(method, scope)) end end
#difference(a, b) (private)
[ GitHub ]# File 'activerecord/lib/active_record/associations/has_many_association.rb', line 158
def difference(a, b) a - b end
#handle_dependency
[ GitHub ]# File 'activerecord/lib/active_record/associations/has_many_association.rb', line 14
def handle_dependency case [:dependent] when :restrict_with_exception raise ActiveRecord::DeleteRestrictionError.new(reflection.name) unless empty? when :restrict_with_error unless empty? record = owner.class.human_attribute_name(reflection.name).downcase owner.errors.add(:base, :'restrict_dependent_destroy.has_many', record: record) throw(:abort) end when :destroy # No point in executing the counter update since we're going to destroy the parent anyway load_target.each { |t| t.destroyed_by_association = reflection } destroy_all when :destroy_async load_target.each do |t| t.destroyed_by_association = reflection end unless target.empty? association_class = target.first.class if association_class.query_constraints_list primary_key_column = association_class.query_constraints_list ids = target.collect { |assoc| primary_key_column.map { |col| assoc.public_send(col) } } else primary_key_column = association_class.primary_key ids = target.collect { |assoc| assoc.public_send(primary_key_column) } end ids.each_slice(owner.class.destroy_association_async_batch_size || ids.size) do |ids_batch| enqueue_destroy_association( owner_model_name: owner.class.to_s, owner_id: owner.id, association_class: reflection.klass.to_s, association_ids: ids_batch, association_primary_key_column: primary_key_column, ensuring_owner_was_method: .fetch(:ensuring_owner_was, nil) ) end end else delete_all end end
#insert_record(record, validate = true, raise = false)
[ GitHub ]# File 'activerecord/lib/active_record/associations/has_many_association.rb', line 61
def insert_record(record, validate = true, raise = false) set_owner_attributes(record) super end
#intersection(a, b) (private)
[ GitHub ]# File 'activerecord/lib/active_record/associations/has_many_association.rb', line 162
def intersection(a, b) a & b end
#update_counter(difference, reflection = reflection()) (private)
[ GitHub ]# File 'activerecord/lib/active_record/associations/has_many_association.rb', line 98
def update_counter(difference, reflection = reflection()) if reflection.has_cached_counter? owner.increment!(reflection.counter_cache_column, difference) end end
#update_counter_if_success(saved_successfully, difference) (private)
[ GitHub ]# File 'activerecord/lib/active_record/associations/has_many_association.rb', line 151
def update_counter_if_success(saved_successfully, difference) if saved_successfully update_counter_in_memory(difference) end saved_successfully end
#update_counter_in_memory(difference, reflection = reflection()) (private)
[ GitHub ]# File 'activerecord/lib/active_record/associations/has_many_association.rb', line 104
def update_counter_in_memory(difference, reflection = reflection()) if reflection.counter_must_be_updated_by_has_many? counter = reflection.counter_cache_column owner.increment(counter, difference) owner.send(:"clear_#{counter}_change") end end