Сопоставление активной Модели записи По Умолчанию

#ruby-on-rails #migration

Вопрос:

Я довольно новичок в Rails, и одна вещь, которую я не понимаю, заключается в том, где в официальной документации rails говорится, что база данных по умолчанию соответствует моделям.

Например, если я помещу значение по умолчанию в столбец при переносе, я ожидаю, что значение по умолчанию будет вставлено для конкретной записи при ее сохранении в базе данных. Однако я заметил, что когда я это делаю Record.new , атрибуты модели уже имеют те значения по умолчанию, которые были установлены в базе данных! Это полезно, потому что это означает, что мне не нужно явно устанавливать его при создании экземпляра нового объекта модели, но где в документах говорится, что происходит автоматическая настройка по умолчанию для новых объектов?

Комментарии:

1. просто думай о деле, есть логический столбец active в foos таблице, теперь на домен несколько классов простираются от Foo ИППП и через (же таблицу Экарт для них), в которой active на уроке бар < Foo-это означает status (перечисления) в режиме [включить, видно], еще один класс active означает целое число Валь > 0, и другое означает значение start_time < Время.сейчас.мирового и так далее … Все они должны быть явно заданы active .

Ответ №1:

но где в документах говорится, что происходит эта автоматическая настройка по умолчанию для новых объектов?

Это не. Низкоуровневая реализация того, как ActiveRecord выполняет волшебство чтения схемы вашей базы данных и перехода оттуда к определению значений по умолчанию при создании новой записи, распределяется по нескольким API — интерфейсам-некоторые из них внутренние. Если вы хотите знать, как это работает в деталях, вам нужно покопаться в коде. Но вам действительно не нужно знать это, чтобы писать приложения Rails.

Что вам действительно нужно знать, так это то, что ActiveRecord считывает схему из базы данных через адаптер базы данных при первой оценке класса. Эта информация о схеме кэшируется в классе, чтобы AR не приходилось повторно запрашивать базу данных, и содержит информацию о типе и значениях по умолчанию столбцов базы данных.

Эта информация затем используется для определения кэша столбцов в модели и атрибутов, которые являются очень расплывчатым термином для метаданных, хранящихся об атрибуте, его значениях до и после приведения типов, а также установщиках и получателях, которые вы используете для доступа к ним. Не обманывайтесь тем, что это каким — либо образом ведет себя как простая переменная экземпляра-ваш foo атрибут не хранится внутри @foo .

ActiveRecord/ActiveModel знает, как установить значения по умолчанию при создании экземпляра модели, поскольку он просматривает атрибуты модели.

ActiveRecord::ModelSchema который является внутренним API, в значительной степени отвечает за сопоставление схемы БД с кэшем столбцов в модели:

 # frozen_string_literal: true

require "monitor"

module ActiveRecord
  module ModelSchema
    # ...
    module ClassMethods
      # ....
      # Returns a hash where the keys are column names and the values are
      # default values when instantiating the Active Record object for this table.
      def column_defaults
        load_schema
        @column_defaults ||= _default_attributes.deep_dup.to_hash.freeze
      end

      # ...

      private
        def inherited(child_class)
          super
          child_class.initialize_load_schema_monitor
        end

        def schema_loaded?
          defined?(@schema_loaded) amp;amp; @schema_loaded
        end

        def load_schema
          return if schema_loaded?
          @load_schema_monitor.synchronize do
            return if defined?(@columns_hash) amp;amp; @columns_hash

            load_schema!

            @schema_loaded = true
          rescue
            reload_schema_from_cache # If the schema loading failed half way through, we must reset the state.
            raise
          end
        end

        def load_schema!
          unless table_name
            raise ActiveRecord::TableNotSpecified, "#{self} has no table configured. Set one with #{self}.table_name="
          end

          columns_hash = connection.schema_cache.columns_hash(table_name)
          columns_hash = columns_hash.except(*ignored_columns) unless ignored_columns.empty?
          @columns_hash = columns_hash.freeze
          @columns_hash.each do |name, column|
            type = connection.lookup_cast_type_from_column(column)
            type = _convert_type_from_options(type)
            warn_if_deprecated_type(column)
            define_attribute(
              name,
              type,
              default: column.default,
              user_provided_default: false
            )
          end
        end
    end
  end
end
 

После этого ActiveRecord::Attributes берет на себя определение фактических атрибутов, с которыми вы взаимодействуете с помощью установщиков и получателей из кэша схемы. Кроме того, здесь настоящая магия происходит в методах с очень небольшим количеством документации, которую следует ожидать для внутреннего API:

 module ActiveRecord
  # See ActiveRecord::Attributes::ClassMethods for documentation
  module Attributes
    extend ActiveSupport::Concern

    included do
      class_attribute :attributes_to_define_after_schema_loads, instance_accessor: false, default: {} # :internal:
    end

    module ClassMethods
      # This is the low level API which sits beneath  attribute . It only
      # accepts type objects, and will do its work immediately instead of
      # waiting for the schema to load. Automatic schema detection and
      # ClassMethods#attribute both call this under the hood. While this method
      # is provided so it can be used by plugin authors, application code
      # should probably use ClassMethods#attribute.
      #
      #  name  The name of the attribute being defined. Expected to be a  String .
      #
      #  cast_type  The type object to use for this attribute.
      #
      #  default  The default value to use when no value is provided. If this option
      # is not passed, the previous default value (if any) will be used.
      # Otherwise, the default will be  nil . A proc can also be passed, and
      # will be called once each time a new value is needed.
      #
      #  user_provided_default  Whether the default value should be cast using
      #  cast  or  deserialize .
      def define_attribute(
        name,
        cast_type,
        default: NO_DEFAULT_PROVIDED,
        user_provided_default: true
      )
        attribute_types[name] = cast_type
        define_default_attribute(name, default, cast_type, from_user: user_provided_default)
      end

      def load_schema! # :nodoc:
        super
        attributes_to_define_after_schema_loads.each do |name, (type, options)|
          define_attribute(name, _lookup_cast_type(name, type, options), **options.slice(:default))
        end
      end
    # ...
  end
end
 

Развенчание стойкого мифа schema.rb никоим образом не связано.

Комментарии:

1. Я думаю, что фактическая настройка атрибутов по умолчанию происходит в ActiveModel::Атрибуты#инициализация . Не уверен на 100%. ActiveRecord (который находится поверх ActiveModel) может иметь свой собственный метод.