module ActiveRecord::Persistence
Наследование Active Record
Публичные методы экземпляра
# File activerecord/lib/active_record/persistence.rb, line 814
def becomes(klass)
became = klass.allocate
became.send(:initialize) do |becoming|
@attributes.reverse_merge!(becoming.instance_variable_get(:@attributes))
becoming.instance_variable_set(:@attributes, @attributes)
becoming.instance_variable_set(:@mutations_from_database, @mutations_from_database ||= nil)
becoming.instance_variable_set(:@new_record, new_record?)
becoming.instance_variable_set(:@destroyed, destroyed?)
becoming.errors.copy!(errors)
end
became
end Возвращает экземпляр указанного klass с атрибутами текущей записи. Это в основном полезно в отношении структур наследования одной таблицы (STI), где вы хотите, чтобы подкласс отображался как суперкласс. Это может использоваться вместе с идентификацией записей в Action Pack, чтобы позволить, скажем, Client < Company сделать что-то вроде render partial: @client.becomes(Company), чтобы отобразить этот экземпляр, используя companies/company partial вместо clients/client.
Примечание: новый экземпляр будет совместно использовать ссылку на те же атрибуты, что и исходный класс. Поэтому значение столбца STI останется прежним. Любое изменение атрибутов в любом из экземпляров повлияет на оба экземпляра. Это включает в себя любую инициализацию атрибутов, выполненную новым экземпляром.
Если вы также хотите изменить столбец STI, используйте becomes! вместо этого.
# File activerecord/lib/active_record/persistence.rb, line 835
def becomes!(klass)
became = becomes(klass)
sti_type = nil
if !klass.descends_from_active_record?
sti_type = klass.sti_name
end
became.public_send("#{klass.inheritance_column}=", sti_type)
became
end Обёртка вокруг becomes, которая также изменяет значение столбца STI экземпляра. Это особенно полезно, если вы хотите сохранить изменённый класс в базе данных.
Примечание: значение столбца STI старого экземпляра также будет изменено, поскольку оба объекта используют один и тот же набор атрибутов.
# File activerecord/lib/active_record/persistence.rb, line 982 def decrement(attribute, by = 1) increment(attribute, -by) end
Инициализирует attribute в ноль, если nil, и вычитает значение, переданное как by (по умолчанию 1). Уменьшение выполняется непосредственно по базовому атрибуту, сеттер не вызывается. Имеет смысл только для числовых атрибутов. Возвращает self.
# File activerecord/lib/active_record/persistence.rb, line 992 def decrement!(attribute, by = 1, touch: nil) increment!(attribute, -by, touch: touch) end
Обёртка вокруг decrement, которая записывает обновление в базу данных. Обновляется только attribute; сама запись не сохраняется. Это означает, что любые другие изменённые атрибуты всё ещё будут считаться изменёнными. Validations и обратные вызовы пропускаются. Поддерживает опцию touch из update_counters, см. подробнее. Возвращает self.
# File activerecord/lib/active_record/persistence.rb, line 766 def delete _delete_row if persisted? @destroyed = true @previously_new_record = false freeze end
Удаляет запись в базе данных и замораживает этот экземпляр, чтобы отразить, что изменения не должны вноситься (поскольку они не могут быть сохранены). Возвращает замороженный экземпляр.
Строка просто удаляется с помощью оператора SQL DELETE по первичному ключу записи, и обратные вызовы не выполняются.
Обратите внимание, что это также удалит записи, помеченные как #readonly?.
Чтобы применить обратные вызовы before_destroy и after_destroy объекта или любые параметры ассоциации :dependent, используйте destroy.
# File activerecord/lib/active_record/persistence.rb, line 780 def destroy _raise_readonly_record_error if readonly? destroy_associations @_trigger_destroy_callback ||= persisted? && destroy_row > 0 @destroyed = true @previously_new_record = false freeze end
Удаляет запись в базе данных и замораживает этот экземпляр, чтобы отразить, что изменения не должны вноситься (поскольку они не могут быть сохранены).
Существует ряд обратных вызовов, связанных с destroy. Если обратный вызов before_destroy вызывает :abort, действие отменяется, и destroy возвращает false. См. ActiveRecord::Callbacks для получения дополнительной информации.
# File activerecord/lib/active_record/persistence.rb, line 796 def destroy! destroy || _raise_record_not_destroyed end
Удаляет запись в базе данных и замораживает этот экземпляр, чтобы отразить, что изменения не должны вноситься (поскольку они не могут быть сохранены).
Существует ряд обратных вызовов, связанных с destroy!. Если обратный вызов before_destroy вызывает :abort, действие отменяется, и destroy! вызывает ActiveRecord::RecordNotDestroyed. См. ActiveRecord::Callbacks для получения дополнительной информации.
# File activerecord/lib/active_record/persistence.rb, line 682 def destroyed? @destroyed end
Возвращает true, если этот объект был уничтожен, в противном случае возвращает false.
# File activerecord/lib/active_record/persistence.rb, line 959 def increment(attribute, by = 1) self[attribute] ||= 0 self[attribute] += by self end
Инициализирует attribute в ноль, если nil, и добавляет значение, переданное как by (по умолчанию 1). Увеличение выполняется непосредственно по базовому атрибуту, сеттер не вызывается. Имеет смысл только для числовых атрибутов. Возвращает self.
# File activerecord/lib/active_record/persistence.rb, line 971
def increment!(attribute, by = 1, touch: nil)
increment(attribute, by)
change = public_send(attribute) - (public_send(:"#{attribute}_in_database") || 0)
self.class.update_counters(id, attribute => change, touch: touch)
public_send(:"clear_#{attribute}_change")
self
end Обёртка вокруг increment, которая записывает обновление в базу данных. Обновляется только attribute; сама запись не сохраняется. Это означает, что любые другие изменённые атрибуты всё ещё будут считаться изменёнными. Validations и обратные вызовы пропускаются. Поддерживает опцию touch из update_counters, см. подробнее. Возвращает self.
# File activerecord/lib/active_record/persistence.rb, line 665 def new_record? @new_record end
Возвращает true, если этот объект ещё не сохранён – то есть запись для объекта ещё не существует в базе данных; в противном случае возвращает false.
# File activerecord/lib/active_record/persistence.rb, line 688 def persisted? !(@new_record || @destroyed) end
Возвращает true, если запись сохранена, т.е. это не новая запись и она не была уничтожена, в противном случае возвращает false.
# File activerecord/lib/active_record/persistence.rb, line 672 def previously_new_record? @previously_new_record end
Возвращает true, если этот объект только что был создан – то есть до последнего обновления или удаления объект не существовал в базе данных, и new_record? вернул бы true.
# File activerecord/lib/active_record/persistence.rb, line 677 def previously_persisted? !new_record? && destroyed? end
Возвращает true, если этот объект был ранее сохранен, но теперь он был удален.
# File activerecord/lib/active_record/persistence.rb, line 1069
def reload(options = nil)
self.class.connection.clear_query_cache
fresh_object = if apply_scoping?(options)
_find_record((options || {}).merge(all_queries: true))
else
self.class.unscoped { _find_record(options) }
end
@association_cache = fresh_object.instance_variable_get(:@association_cache)
@attributes = fresh_object.instance_variable_get(:@attributes)
@new_record = false
@previously_new_record = false
self
end Перезагружает запись из базы данных.
Этот метод находит запись по её первичному ключу (который может быть назначен вручную) и изменяет получатель на месте:
account = Account.new # => #<Account id: nil, email: nil> account.id = 1 account.reload # Account Load (1.2ms) SELECT "accounts".* FROM "accounts" WHERE "accounts"."id" = $1 LIMIT 1 [["id", 1]] # => #<Account id: 1, email: 'account@example.com'>
Attributes перезагружаются из базы данных, и кэши очищаются, в частности кэш ассоциаций и QueryCache.
Если запись больше не существует в базе данных ActiveRecord::RecordNotFound генерируется исключение. В противном случае, в дополнение к изменению на месте метод возвращает self для удобства.
Необязательный флаг параметра :lock позволяет заблокировать перезагруженную запись:
reload(lock: true) # reload with pessimistic locking
Перезагрузка часто используется в тестовых наборах, чтобы проверить, что данные действительно записаны в базу данных, или когда некоторое действие изменяет соответствующую строку в базе данных, но не объект в памяти:
assert account.deposit!(25) assert_equal 25, account.credit # check it is updated in memory assert_equal 25, account.reload.credit # check it is also persisted
Ещё один распространённый случай использования — обработка оптимистичной блокировки:
def with_optimistic_retry
begin
yield
rescue ActiveRecord::StaleObjectError
begin
# Reload lock_version in particular.
reload
rescue ActiveRecord::RecordNotFound
# If the record is gone there is nothing to do.
else
retry
end
end
end
# File activerecord/lib/active_record/persistence.rb, line 717 def save(**options, &block) create_or_update(**options, &block) rescue ActiveRecord::RecordInvalid false end
Сохраняет модель.
Если модель новая, запись создаётся в базе данных, в противном случае существующая запись обновляется.
По умолчанию save всегда запускает проверки. Если какая-либо из них не пройдена, действие отменяется, и save возвращает false, и запись не будет сохранена. Однако, если вы предоставите validate: false, проверки полностью игнорируются. См. ActiveRecord::Validations для получения дополнительной информации.
По умолчанию, save также устанавливает атрибуты updated_at/updated_on на текущее время. Однако, если вы предоставите touch: false, эти метки времени не будут обновлены.
Существует ряд обратных вызовов, связанных с save. Если какой-либо из обратных вызовов before_* генерирует :abort, действие отменяется, и save возвращает false. См. ActiveRecord::Callbacks для получения более подробной информации.
Attributes, помеченные как доступные только для чтения, игнорируются, если запись обновляется.
# File activerecord/lib/active_record/persistence.rb, line 750
def save!(**options, &block)
create_or_update(**options, &block) || raise(RecordNotSaved.new("Failed to save the record", self))
end Сохраняет модель.
Если модель новая, запись создаётся в базе данных, в противном случае существующая запись обновляется.
По умолчанию, save! всегда запускает проверки. Если какая-либо из них не пройдена, генерируется исключение ActiveRecord::RecordInvalid, и запись не будет сохранена. Однако, если вы предоставите validate: false, проверки полностью игнорируются. См. ActiveRecord::Validations для получения дополнительной информации.
По умолчанию, save! также устанавливает атрибуты updated_at/updated_on на текущее время. Однако, если вы предоставите touch: false, эти метки времени не будут обновлены.
Существует ряд обратных вызовов, связанных с save!. Если какой-либо из обратных вызовов before_* генерирует :abort, действие отменяется, и save! генерирует исключение ActiveRecord::RecordNotSaved. См. ActiveRecord::Callbacks для получения более подробной информации.
Attributes, помеченные как доступные только для чтения, игнорируются, если запись обновляется.
Если исключение не сгенерировано, возвращает true.
# File activerecord/lib/active_record/persistence.rb, line 1008
def toggle(attribute)
self[attribute] = !public_send("#{attribute}?")
self
end Присваивает attribute булево значение, противоположное attribute?. Таким образом, если предикат возвращает true, атрибут станет false. Этот метод переключает непосредственно базовое значение без вызова любого сеттера. Возвращает self.
Пример:
user = User.first user.banned? # => false user.toggle(:banned) user.banned? # => true
# File activerecord/lib/active_record/persistence.rb, line 1017 def toggle!(attribute) toggle(attribute).update_attribute(attribute, self[attribute]) end
Обёртка вокруг toggle, которая сохраняет запись. Этот метод отличается от своей не-bang версии тем, что он проходит через установщик атрибута. Сохранение не подвергается проверкам на валидность. Возвращает true, если запись может быть сохранена.
# File activerecord/lib/active_record/persistence.rb, line 1119
def touch(*names, time: nil)
_raise_record_not_touched_error unless persisted?
_raise_readonly_record_error if readonly?
attribute_names = timestamp_attributes_for_update_in_model
attribute_names = (attribute_names | names).map! do |name|
name = name.to_s
name = self.class.attribute_aliases[name] || name
verify_readonly_attribute(name)
name
end
unless attribute_names.empty?
affected_rows = _touch_row(attribute_names, time)
@_trigger_update_callback = affected_rows == 1
else
true
end
end Сохраняет запись с атрибутами updated_at/on, установленными на текущее время или указанное время. Обратите внимание, что проверка не выполняется и выполняются только обратные вызовы after_touch, after_commit и after_rollback.
Этому методу могут быть переданы имена атрибутов и необязательный аргумент времени. Если переданы имена атрибутов, они обновляются вместе с атрибутами updated_at/on. Если аргумент времени не передан, по умолчанию используется текущее время.
product.touch # updates updated_at/on with current time product.touch(time: Time.new(2015, 2, 16, 0, 0, 0)) # updates updated_at/on with specified time product.touch(:designed_at) # updates the designed_at attribute and updated_at/on product.touch(:started_at, :ended_at) # updates started_at, ended_at and updated_at/on attributes
Если используется вместе с belongs_to, то touch вызовет метод touch для связанного объекта.
class Brake < ActiveRecord::Base belongs_to :car, touch: true end class Car < ActiveRecord::Base belongs_to :corporation, touch: true end # triggers @brake.car.touch and @brake.car.corporation.touch @brake.touch
Обратите внимание, что touch должен использоваться для сохраняемого объекта, иначе будет сгенерировано исключение ActiveRecordError. Например:
ball = Ball.new ball.touch(:updated_at) # => raises ActiveRecordError
# File activerecord/lib/active_record/persistence.rb, line 890
def update(attributes)
# The following transaction covers any possible database side-effects of the
# attributes assignment. For example, setting the IDs of a child collection.
with_transaction_returning_status do
assign_attributes(attributes)
save
end
end Обновляет атрибуты модели из переданного хэша и сохраняет запись, всё это заключено в транзакцию. Если объект неверен, сохранение завершится неудачей и будет возвращено false.
# File activerecord/lib/active_record/persistence.rb, line 901
def update!(attributes)
# The following transaction covers any possible database side-effects of the
# attributes assignment. For example, setting the IDs of a child collection.
with_transaction_returning_status do
assign_attributes(attributes)
save!
end
end Обновляет свой получатель так же, как update, но вызывает save! вместо save, поэтому генерируется исключение, если запись неверна и сохранение завершится неудачей.
# File activerecord/lib/active_record/persistence.rb, line 857
def update_attribute(name, value)
name = name.to_s
verify_readonly_attribute(name)
public_send("#{name}=", value)
save(validate: false)
end Обновляет один атрибут и сохраняет запись. Это особенно полезно для булевых флагов в существующих записях. Также обратите внимание, что
-
Проверка пропускается.
-
Вызываются обратные вызовы.
-
Столбец updated_at/updated_on обновляется, если этот столбец доступен.
-
Обновляет все атрибуты, которые являются изменёнными в этом объекте.
Этот метод генерирует исключение ActiveRecord::ActiveRecordError, если атрибут помечен как доступный только для чтения.
См. также update_column.
# File activerecord/lib/active_record/persistence.rb, line 879
def update_attribute!(name, value)
name = name.to_s
verify_readonly_attribute(name)
public_send("#{name}=", value)
save!(validate: false)
end Обновляет один атрибут и сохраняет запись. Это особенно полезно для булевых флагов в существующих записях. Также обратите внимание, что
-
Проверка пропускается.
-
Вызываются обратные вызовы.
-
Столбец updated_at/updated_on обновляется, если этот столбец доступен.
-
Обновляет все атрибуты, которые являются изменёнными в этом объекте.
Этот метод генерирует исключение ActiveRecord::ActiveRecordError, если атрибут помечен как доступный только для чтения.
Если какой-либо из обратных вызовов before_* генерирует :abort, действие отменяется, и update_attribute! генерирует исключение ActiveRecord::RecordNotSaved. См. ActiveRecord::Callbacks для получения более подробной информации.
# File activerecord/lib/active_record/persistence.rb, line 911 def update_column(name, value) update_columns(name => value) end
Эквивалентно update_columns(name => value).
# File activerecord/lib/active_record/persistence.rb, line 931
def update_columns(attributes)
raise ActiveRecordError, "cannot update a new record" if new_record?
raise ActiveRecordError, "cannot update a destroyed record" if destroyed?
_raise_readonly_record_error if readonly?
attributes = attributes.transform_keys do |key|
name = key.to_s
name = self.class.attribute_aliases[name] || name
verify_readonly_attribute(name) || name
end
update_constraints = _query_constraints_hash
attributes = attributes.each_with_object({}) do |(k, v), h|
h[k] = @attributes.write_cast_value(k, v)
clear_attribute_change(k)
end
affected_rows = self.class._update_record(
attributes,
update_constraints
)
affected_rows == 1
end Обновляет атрибуты напрямую в базе данных, выполняя оператор UPDATE SQL, и устанавливает их в получателе:
user.update_columns(last_request_at: Time.current)
Это самый быстрый способ обновления атрибутов, поскольку он идет напрямую в базу данных, но следует учитывать, что в результате стандартные процедуры обновления полностью проигнорированы. В частности:
-
Проверка не выполняется.
-
Обработчики не вызываются.
-
updated_at/updated_onне обновляются. -
Однако, атрибуты сериализуются по тем же правилам, что и
ActiveRecord::Relation#update_all
Этот метод вызывает исключение ActiveRecord::ActiveRecordError, если он вызван для новых объектов или если хотя бы один из атрибутов помечен как только для чтения.
© 2004–2021 David Heinemeier Hansson
Licensed under the MIT License.