123456789_123456789_123456789_123456789_123456789_

Class: ActiveRecord::Associations::BelongsToAssociation

Do not use. This class is for internal use only.
Relationships & Source Files
Extension / Inclusion / Inheritance Descendants
Subclasses:
Super Chains via Extension / Inclusion / Inheritance
Class Chain:
Instance Chain:
Inherits: ActiveRecord::Associations::SingularAssociation
Defined in: activerecord/lib/active_record/associations/belongs_to_association.rb

Overview

Active Record Belongs To Association

Class Method Summary

Association - Inherited

Instance Attribute Summary

Association - Inherited

#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 \target, and the loaded flag to true.

#find_target, #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

SingularAssociation - Inherited

#build,
#force_reload_reader

Implements the reload reader method, e.g.

#reader

Implements the reader method, e.g.

#writer

Implements the writer method, e.g.

#_create_record, #find_target, #replace, #scope_for_create, #set_new_record

Association - Inherited

#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 true.

#marshal_dump

We can’t dump @reflection and @through_reflection since it contains the scope proc.

#marshal_load,
#reload

Reloads the target and returns self on success.

#remove_inverse_instance

Remove the inverse association, if possible.

#reset

Resets the loaded flag to false and sets the target to nil.

#reset_negative_cache, #reset_scope, #scope,
#set_inverse_instance

Set the inverse association, if possible.

#set_inverse_instance_from_queries,
#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.

#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 ::ActiveRecord::AssociationTypeMismatch unless record is of the kind of the class of the associated objects.

#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 ThroughAssociation) to merge in other scopes (i.e.

Constructor Details

This class inherits a constructor from ActiveRecord::Associations::Association

Instance Attribute Details

#find_target?Boolean (readonly, private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/associations/belongs_to_association.rb', line 118

def find_target?
  !loaded? && foreign_key_present? && klass
end

#foreign_key_present?Boolean (readonly, private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/associations/belongs_to_association.rb', line 141

def foreign_key_present?
  Array(reflection.foreign_key).all? { |fk| owner._read_attribute(fk) }
end

#require_counter_update?Boolean (readonly, private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/associations/belongs_to_association.rb', line 122

def require_counter_update?
  reflection.counter_cache_column && owner.persisted?
end

#saved_change_to_target?Boolean (readonly)

[ GitHub ]

  
# File 'activerecord/lib/active_record/associations/belongs_to_association.rb', line 84

def saved_change_to_target?
  owner.saved_change_to_attribute?(reflection.foreign_key)
end

#target_changed?Boolean (readonly)

[ GitHub ]

  
# File 'activerecord/lib/active_record/associations/belongs_to_association.rb', line 76

def target_changed?
  owner.attribute_changed?(reflection.foreign_key) || (!foreign_key_present? && target&.new_record?)
end

#target_previously_changed?Boolean (readonly)

[ GitHub ]

  
# File 'activerecord/lib/active_record/associations/belongs_to_association.rb', line 80

def target_previously_changed?
  owner.attribute_previously_changed?(reflection.foreign_key)
end

#updated?Boolean (readonly)

[ GitHub ]

  
# File 'activerecord/lib/active_record/associations/belongs_to_association.rb', line 49

def updated?
  @updated
end

Instance Method Details

#decrement_counters

[ GitHub ]

  
# File 'activerecord/lib/active_record/associations/belongs_to_association.rb', line 53

def decrement_counters
  update_counters(-1)
end

#decrement_counters_before_last_save

[ GitHub ]

  
# File 'activerecord/lib/active_record/associations/belongs_to_association.rb', line 61

def decrement_counters_before_last_save
  if reflection.polymorphic?
    model_type_was = owner.attribute_before_last_save(reflection.foreign_type)
    model_was = owner.class.polymorphic_class_for(model_type_was) if model_type_was
  else
    model_was = klass
  end

  foreign_key_was = owner.attribute_before_last_save(reflection.foreign_key)

  if foreign_key_was && model_was < ActiveRecord::Base
    update_counters_via_scope(model_was, foreign_key_was, -1)
  end
end

#default(&block)

[ GitHub ]

  
# File 'activerecord/lib/active_record/associations/belongs_to_association.rb', line 40

def default(&block)
  writer(owner.instance_exec(&block)) if reader.nil?
end

#handle_dependency

[ GitHub ]

  
# File 'activerecord/lib/active_record/associations/belongs_to_association.rb', line 7

def handle_dependency
  return unless load_target

  case options[:dependent]
  when :destroy
    raise ActiveRecord::Rollback unless target.destroy
  when :destroy_async
    if reflection.foreign_key.is_a?(Array)
      primary_key_column = reflection.active_record_primary_key.map(&:to_sym)
      id = reflection.foreign_key.map { |col| owner.public_send(col.to_sym) }
    else
      primary_key_column = reflection.active_record_primary_key.to_sym
      id = owner.public_send(reflection.foreign_key.to_sym)
    end

    enqueue_destroy_association(
      owner_model_name: owner.class.to_s,
      owner_id: owner.id,
      association_class: reflection.klass.to_s,
      association_ids: [id],
      association_primary_key_column: primary_key_column,
      ensuring_owner_was_method: options.fetch(:ensuring_owner_was, nil)
    )
  else
    target.public_send(options[:dependent])
  end
end

#increment_counters

[ GitHub ]

  
# File 'activerecord/lib/active_record/associations/belongs_to_association.rb', line 57

def increment_counters
  update_counters(1)
end

#inversed_from(record)

[ GitHub ]

  
# File 'activerecord/lib/active_record/associations/belongs_to_association.rb', line 35

def inversed_from(record)
  replace_keys(record)
  super
end

#invertible_for?(record) ⇒ Boolean (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/associations/belongs_to_association.rb', line 145

def invertible_for?(record)
  inverse = inverse_reflection_for(record)
  inverse && (inverse.has_one? || inverse.klass.has_many_inversing)
end

#primary_key(klass) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/associations/belongs_to_association.rb', line 137

def primary_key(klass)
  reflection.association_primary_key(klass)
end

#replace(record) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/associations/belongs_to_association.rb', line 89

def replace(record)
  if record
    raise_on_type_mismatch!(record)
    set_inverse_instance(record)
    @updated = true
  elsif target
    remove_inverse_instance(target)
  end

  replace_keys(record, force: true)

  self.target = record
end

#replace_keys(record, force: false) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/associations/belongs_to_association.rb', line 126

def replace_keys(record, force: false)
  target_key_values = record ? Array(primary_key(record.class)).map { |key| record._read_attribute(key) } : []
  reflection_fk = Array(reflection.foreign_key)

  if force || reflection_fk.map { |fk| owner._read_attribute(fk) } != target_key_values
    reflection_fk.zip(target_key_values).each do |key, value|
      owner[key] = value
    end
  end
end

#reset

[ GitHub ]

  
# File 'activerecord/lib/active_record/associations/belongs_to_association.rb', line 44

def reset
  super
  @updated = false
end

#stale_state (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/associations/belongs_to_association.rb', line 150

def stale_state
  result = owner._read_attribute(reflection.foreign_key) { |n| owner.send(:missing_attribute, n, caller) }
  result && result.to_s
end

#update_counters(by) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/associations/belongs_to_association.rb', line 103

def update_counters(by)
  if require_counter_update? && foreign_key_present?
    if target && !stale_target?
      target.increment!(reflection.counter_cache_column, by, touch: reflection.options[:touch])
    else
      update_counters_via_scope(klass, owner._read_attribute(reflection.foreign_key), by)
    end
  end
end

#update_counters_via_scope(klass, foreign_key, by) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/associations/belongs_to_association.rb', line 113

def update_counters_via_scope(klass, foreign_key, by)
  scope = klass.unscoped.where!(primary_key(klass) => foreign_key)
  scope.update_counters(reflection.counter_cache_column => by, touch: reflection.options[:touch])
end