#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) может иметь свой собственный метод.