method
derive_fk_query_constraints
v7.1.3.4 -
Show latest stable
-
0 notes -
Class: ActiveRecord::Reflection::AssociationReflection
- 1.0.0
- 1.1.6
- 1.2.6
- 2.0.3
- 2.1.0
- 2.2.1
- 2.3.8
- 3.0.0
- 3.0.9
- 3.1.0
- 3.2.1
- 3.2.8
- 3.2.13
- 4.0.2
- 4.1.8
- 4.2.1
- 4.2.7
- 4.2.9
- 5.0.0.1
- 5.1.7
- 5.2.3
- 6.0.0
- 6.1.3.1
- 6.1.7.7
- 7.0.0
- 7.1.3.2 (0)
- 7.1.3.4 (0)
- What's this?
derive_fk_query_constraints(foreign_key)
private
Hide source
# File activerecord/lib/active_record/reflection.rb, line 785 def derive_fk_query_constraints(foreign_key) primary_query_constraints = active_record.query_constraints_list owner_pk = active_record.primary_key if primary_query_constraints.size != 2 raise ArgumentError, <<~MSG.squish The query constraints list on the `#{active_record}` model has more than 2 attributes. Active Record is unable to derive the query constraints for the association. You need to explicitly define the query constraints for this association. MSG end if !primary_query_constraints.include?(owner_pk) raise ArgumentError, <<~MSG.squish The query constraints on the `#{active_record}` model does not include the primary key so Active Record is unable to derive the foreign key constraints for the association. You need to explicitly define the query constraints for this association. MSG end first_key, last_key = primary_query_constraints if first_key == owner_pk [foreign_key, last_key.to_s] elsif last_key == owner_pk [first_key.to_s, foreign_key] else raise ArgumentError, <<~MSG.squish Active Record couldn't correctly interpret the query constraints for the `#{active_record}` model. The query constraints on `#{active_record}` are `#{primary_query_constraints}` and the foreign key is `#{foreign_key}`. You need to explicitly set the query constraints for this association. MSG end end def derive_join_table ModelSchema.derive_join_table_name active_record.table_name, klass.table_name end end class HasManyReflection < AssociationReflection # :nodoc: def macro; :has_many; end def collection?; true; end def association_class if options[:through] Associations::HasManyThroughAssociation else Associations::HasManyAssociation end end end class HasOneReflection < AssociationReflection # :nodoc: def macro; :has_one; end def has_one?; true; end def association_class if options[:through] Associations::HasOneThroughAssociation else Associations::HasOneAssociation end end end class BelongsToReflection < AssociationReflection # :nodoc: def macro; :belongs_to; end def belongs_to?; true; end def association_class if polymorphic? Associations::BelongsToPolymorphicAssociation else Associations::BelongsToAssociation end end # klass option is necessary to support loading polymorphic associations def association_primary_key(klass = nil) if primary_key = options[:primary_key] @association_primary_key ||= -primary_key.to_s elsif (klass || self.klass).has_query_constraints? || options[:query_constraints] (klass || self.klass).composite_query_constraints_list elsif (klass || self.klass).composite_primary_key? # If klass has composite primary key of shape [:<tenant_key>, :id], infer primary_key as :id primary_key = (klass || self.klass).primary_key primary_key.include?("id") ? "id" : primary_key else primary_key(klass || self.klass) end end def join_primary_key(klass = nil) polymorphic? ? association_primary_key(klass) : association_primary_key end def join_foreign_key foreign_key end def join_foreign_type foreign_type end private def can_find_inverse_of_automatically?(*) !polymorphic? && super end end class HasAndBelongsToManyReflection < AssociationReflection # :nodoc: def macro; :has_and_belongs_to_many; end def collection? true end end # Holds all the metadata about a :through association as it was specified # in the Active Record class. class ThroughReflection < AbstractReflection # :nodoc: delegate :foreign_key, :foreign_type, :association_foreign_key, :join_id_for, :type, :active_record_primary_key, :join_foreign_key, to: :source_reflection def initialize(delegate_reflection) super() @delegate_reflection = delegate_reflection @klass = delegate_reflection.options[:anonymous_class] @source_reflection_name = delegate_reflection.options[:source] ensure_option_not_given_as_class!(:source_type) end def through_reflection? true end def klass @klass ||= delegate_reflection.compute_class(class_name) end # Returns the source of the through reflection. It checks both a singularized # and pluralized form for <tt>:belongs_to</tt> or <tt>:has_many</tt>. # # class Post < ActiveRecord::Base # has_many :taggings # has_many :tags, through: :taggings # end # # class Tagging < ActiveRecord::Base # belongs_to :post # belongs_to :tag # end # # tags_reflection = Post.reflect_on_association(:tags) # tags_reflection.source_reflection # # => <ActiveRecord::Reflection::BelongsToReflection: @name=:tag, @active_record=Tagging, @plural_name="tags"> # def source_reflection through_reflection.klass._reflect_on_association(source_reflection_name) end # Returns the AssociationReflection object specified in the <tt>:through</tt> option # of a HasManyThrough or HasOneThrough association. # # class Post < ActiveRecord::Base # has_many :taggings # has_many :tags, through: :taggings # end # # tags_reflection = Post.reflect_on_association(:tags) # tags_reflection.through_reflection # # => <ActiveRecord::Reflection::HasManyReflection: @name=:taggings, @active_record=Post, @plural_name="taggings"> # def through_reflection active_record._reflect_on_association(options[:through]) end # Returns an array of reflections which are involved in this association. Each item in the # array corresponds to a table which will be part of the query for this association. # # The chain is built by recursively calling #chain on the source reflection and the through # reflection. The base case for the recursion is a normal association, which just returns # [self] as its #chain. # # class Post < ActiveRecord::Base # has_many :taggings # has_many :tags, through: :taggings # end # # tags_reflection = Post.reflect_on_association(:tags) # tags_reflection.chain # # => [<ActiveRecord::Reflection::ThroughReflection: @delegate_reflection=#<ActiveRecord::Reflection::HasManyReflection: @name=:tags...>, # <ActiveRecord::Reflection::HasManyReflection: @name=:taggings, @options={}, @active_record=Post>] # def collect_join_chain collect_join_reflections [self] end # This is for clearing cache on the reflection. Useful for tests that need to compare # SQL queries on associations. def clear_association_scope_cache # :nodoc: delegate_reflection.clear_association_scope_cache source_reflection.clear_association_scope_cache through_reflection.clear_association_scope_cache end def scopes source_reflection.scopes + super end def join_scopes(table, predicate_builder, klass = self.klass, record = nil) # :nodoc: source_reflection.join_scopes(table, predicate_builder, klass, record) + super end def has_scope? scope || options[:source_type] || source_reflection.has_scope? || through_reflection.has_scope? end # A through association is nested if there would be more than one join table def nested? source_reflection.through_reflection? || through_reflection.through_reflection? end # We want to use the klass from this reflection, rather than just delegate straight to # the source_reflection, because the source_reflection may be polymorphic. We still # need to respect the source_reflection's :primary_key option, though. def association_primary_key(klass = nil) # Get the "actual" source reflection if the immediate source reflection has a # source reflection itself if primary_key = actual_source_reflection.options[:primary_key] @association_primary_key ||= -primary_key.to_s else primary_key(klass || self.klass) end end def join_primary_key(klass = self.klass) source_reflection.join_primary_key(klass) end # Gets an array of possible <tt>:through</tt> source reflection names in both singular and plural form. # # class Post < ActiveRecord::Base # has_many :taggings # has_many :tags, through: :taggings # end # # tags_reflection = Post.reflect_on_association(:tags) # tags_reflection.source_reflection_names # # => [:tag, :tags] # def source_reflection_names options[:source] ? [options[:source]] : [name.to_s.singularize, name].uniq end def source_reflection_name # :nodoc: @source_reflection_name ||= begin names = [name.to_s.singularize, name].collect(&:to_sym).uniq names = names.find_all { |n| through_reflection.klass._reflect_on_association(n) } if names.length > 1 raise AmbiguousSourceReflectionForThroughAssociation.new( active_record.name, macro, name, options, source_reflection_names ) end names.first end end def source_options source_reflection.options end def through_options through_reflection.options end def check_validity! if through_reflection.nil? raise HasManyThroughAssociationNotFoundError.new(active_record, self) end if through_reflection.polymorphic? if has_one? raise HasOneAssociationPolymorphicThroughError.new(active_record.name, self) else raise HasManyThroughAssociationPolymorphicThroughError.new(active_record.name, self) end end if source_reflection.nil? raise HasManyThroughSourceAssociationNotFoundError.new(self) end if options[:source_type] && !source_reflection.polymorphic? raise HasManyThroughAssociationPointlessSourceTypeError.new(active_record.name, self, source_reflection) end if source_reflection.polymorphic? && options[:source_type].nil? raise HasManyThroughAssociationPolymorphicSourceError.new(active_record.name, self, source_reflection) end if has_one? && through_reflection.collection? raise HasOneThroughCantAssociateThroughCollection.new(active_record.name, self, through_reflection) end if parent_reflection.nil? reflections = active_record.reflections.keys.map(&:to_sym) if reflections.index(through_reflection.name) > reflections.index(name) raise HasManyThroughOrderError.new(active_record.name, self, through_reflection) end end check_validity_of_inverse! end def constraints scope_chain = source_reflection.constraints scope_chain << scope if scope scope_chain end def add_as_source(seed) collect_join_reflections seed end def add_as_polymorphic_through(reflection, seed) collect_join_reflections(seed + [PolymorphicReflection.new(self, reflection)]) end def add_as_through(seed) collect_join_reflections(seed + [self]) end protected def actual_source_reflection # FIXME: this is a horrible name source_reflection.actual_source_reflection end private attr_reader :delegate_reflection def collect_join_reflections(seed) a = source_reflection.add_as_source seed if options[:source_type] through_reflection.add_as_polymorphic_through self, a else through_reflection.add_as_through a end end def inverse_name; delegate_reflection.send(:inverse_name); end def derive_class_name # get the class_name of the belongs_to association of the through reflection options[:source_type] || source_reflection.class_name end delegate_methods = AssociationReflection.public_instance_methods - public_instance_methods delegate(*delegate_methods, to: :delegate_reflection) end class PolymorphicReflection < AbstractReflection # :nodoc: delegate :klass, :scope, :plural_name, :type, :join_primary_key, :join_foreign_key, :name, :scope_for, to: :@reflection def initialize(reflection, previous_reflection) super() @reflection = reflection @previous_reflection = previous_reflection end def join_scopes(table, predicate_builder, klass = self.klass, record = nil) # :nodoc: scopes = @previous_reflection.join_scopes(table, predicate_builder, klass, record) + super scopes << build_scope(table, predicate_builder, klass).instance_exec(record, &source_type_scope) end def constraints @reflection.constraints + [source_type_scope] end private def source_type_scope type = @previous_reflection.foreign_type source_type = @previous_reflection.options[:source_type] lambda { |object| where(type => source_type) } end end class RuntimeReflection < AbstractReflection # :nodoc: delegate :scope, :type, :constraints, :join_foreign_key, to: :@reflection def initialize(reflection, association) super() @reflection = reflection @association = association end def klass @association.klass end def aliased_table klass.arel_table end def join_primary_key(klass = self.klass) @reflection.join_primary_key(klass) end def all_includes; yield; end end end end