модуль ActiveRecord::Persistence
Active Record Персистентность
Методы открытого экземпляра
Возвращает экземпляр указанного klass с атрибутами текущей записи. Это в основном полезно в отношении структур единой таблицы наследования, где вы хотите, чтобы подкласс отображался как суперкласс. Это можно использовать вместе с идентификацией записей в Action Pack, чтобы, скажем, Client < Company смог сделать что-то вроде render partial: @client.becomes(Company) для отображения этого экземпляра с помощью части companies/company вместо clients/client.
Примечание: новый экземпляр будет разделять ссылку на те же атрибуты, что и исходный класс. Поэтому любое изменение атрибутов в любом экземпляре повлияет на другой.
# File activerecord/lib/active_record/persistence.rb, line 180
def becomes(klass)
became = klass.new
became.instance_variable_set("@attributes", @attributes)
became.instance_variable_set("@attributes_cache", @attributes_cache)
changed_attributes = @changed_attributes if defined?(@changed_attributes)
became.instance_variable_set("@changed_attributes", changed_attributes || {})
became.instance_variable_set("@new_record", new_record?)
became.instance_variable_set("@destroyed", destroyed?)
became.instance_variable_set("@errors", errors)
became
end Обёртка вокруг becomes, которая также изменяет значение столбца sti экземпляра. Это особенно полезно, если вы хотите сохранить изменённый класс в вашей базе данных.
Примечание: значение столбца sti старого экземпляра также будет изменено, так как оба объекта разделяют один и тот же набор атрибутов.
# File activerecord/lib/active_record/persistence.rb, line 198
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 Инициализирует attribute значением ноль, если nil , и вычитает значение, переданное в качестве by (по умолчанию 1). Уменьшение выполняется непосредственно над базовым атрибутом, вызов сеттера не выполняется. Имеет смысл только для числовых атрибутов. Возвращает self.
# File activerecord/lib/active_record/persistence.rb, line 308 def decrement(attribute, by = 1) self[attribute] ||= 0 self[attribute] -= by self end
Обёртка вокруг decrement , которая сохраняет запись. Этот метод отличается от своего варианта без восклицательного знака тем, что он проходит через сеттер атрибута. Сохранение не подлежит проверке валидации. Возвращает true , если запись была сохранена.
# File activerecord/lib/active_record/persistence.rb, line 318 def decrement!(attribute, by = 1) decrement(attribute, by).update_attribute(attribute, self[attribute]) end
Удаляет запись в базе данных и замораживает этот экземпляр, чтобы отразить, что никакие изменения не должны быть внесены (поскольку они не могут быть сохранены). Возвращает замороженный экземпляр.
Строка просто удаляется с помощью SQL DELETE -выражения по первичному ключу записи, и никакие обратные вызовы не выполняются.
Чтобы применить обратные вызовы before_destroy и after_destroy или любые параметры ассоциации :dependent, используйте #destroy.
# File activerecord/lib/active_record/persistence.rb, line 138 def delete self.class.delete(id) if persisted? @destroyed = true freeze end
Удаляет запись в базе данных и замораживает этот экземпляр, чтобы отразить, что никакие изменения не должны быть внесены (поскольку они не могут быть сохранены).
Существует ряд обратных вызовов, связанных с destroy. Если обратный вызов before_destroy вернёт false, действие отменяется и destroy возвращает false. Дополнительные сведения см. в ActiveRecord::Callbacks.
# File activerecord/lib/active_record/persistence.rb, line 151 def destroy raise ReadOnlyRecord if readonly? destroy_associations destroy_row if persisted? @destroyed = true freeze end
Удаляет запись в базе данных и замораживает этот экземпляр, чтобы отразить, что никакие изменения не должны быть внесены (поскольку они не могут быть сохранены).
Существует ряд обратных вызовов, связанных с destroy!. Если обратный вызов before_destroy вернёт false, действие отменяется и destroy! поднимает ActiveRecord::RecordNotDestroyed. Дополнительные сведения см. в ActiveRecord::Callbacks.
# File activerecord/lib/active_record/persistence.rb, line 166 def destroy! destroy || raise(ActiveRecord::RecordNotDestroyed) end
Возвращает true, если этот объект был уничтожен, в противном случае возвращает false.
# File activerecord/lib/active_record/persistence.rb, line 74 def destroyed? sync_with_transaction_state @destroyed end
Инициализирует attribute значением ноль, если nil , и добавляет значение, переданное в качестве by (по умолчанию 1). Увеличение выполняется непосредственно над базовым атрибутом, вызов сеттера не выполняется. Имеет смысл только для числовых атрибутов. Возвращает self.
# File activerecord/lib/active_record/persistence.rb, line 291 def increment(attribute, by = 1) self[attribute] ||= 0 self[attribute] += by self end
Обёртка вокруг increment , которая сохраняет запись. Этот метод отличается от своего варианта без восклицательного знака тем, что он проходит через сеттер атрибута. Сохранение не подлежит проверке валидации. Возвращает true , если запись была сохранена.
# File activerecord/lib/active_record/persistence.rb, line 301 def increment!(attribute, by = 1) increment(attribute, by).update_attribute(attribute, self[attribute]) end
Возвращает true, если этот объект ещё не сохранён — то есть, запись для объекта ещё не существует в базе данных; в противном случае возвращает false.
# File activerecord/lib/active_record/persistence.rb, line 68 def new_record? sync_with_transaction_state @new_record end
Возвращает true, если запись сохранена, т. е. это не новая запись и она не была удалена, в противном случае возвращает false.
# File activerecord/lib/active_record/persistence.rb, line 81 def persisted? !(new_record? || destroyed?) 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'>
Атрибуты перезагружаются из базы данных, и кэши очищаются, в частности, кэш ассоциаций.
Если запись больше не существует в базе данных 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 387
def reload(options = nil)
clear_aggregation_cache
clear_association_cache
fresh_object =
if options && options[:lock]
self.class.unscoped { self.class.lock(options[:lock]).find(id) }
else
self.class.unscoped { self.class.find(id) }
end
@attributes.update(fresh_object.instance_variable_get('@attributes'))
@column_types = self.class.column_types
@column_types_override = fresh_object.instance_variable_get('@column_types_override')
@attributes_cache = {}
@new_record = false
self
end Сохраняет модель.
Если модель новая, в базе данных создаётся запись, в противном случае существующая запись обновляется.
По умолчанию save всегда выполняет валидацию. Если какая-либо из них завершается неудачно, действие отменяется, и save возвращает false. Однако, если вы передадите validate: false, валидация будет пропущена. Дополнительные сведения см. в ActiveRecord::Validations.
Существует ряд обратных вызовов, связанных с save. Если любой из обратных вызовов before_* возвращает false, действие отменяется, и save возвращает false. Дополнительные сведения см. в ActiveRecord::Callbacks.
Атрибуты, помеченные как только для чтения, будут игнорироваться при обновлении записи.
# File activerecord/lib/active_record/persistence.rb, line 102 def save(*) create_or_update rescue ActiveRecord::RecordInvalid false end
Сохраняет модель.
Если модель новая, в базе данных создаётся запись, в противном случае существующая запись обновляется.
При этом всегда выполняются проверки save!. Если какая-либо из них завершится неудачно, генерируется исключение ActiveRecord::RecordInvalid. См. ActiveRecord::Validations для получения дополнительной информации.
Существует ряд обратных вызовов, связанных с save!. Если любой из обратных вызовов before_* вернёт false, действие отменяется и генерируется исключение ActiveRecord::RecordNotSaved методом save!. См. ActiveRecord::Callbacks для получения более подробной информации.
Атрибуты, помеченные как только для чтения, будут игнорироваться при обновлении записи.
# File activerecord/lib/active_record/persistence.rb, line 124 def save!(*) create_or_update || raise(RecordNotSaved) end
Присваивает attribute логическое отрицание attribute?. Таким образом, если предикат возвращает true, атрибут станет false. Этот метод напрямую переключает базовое значение без вызова метода-сеттера. Возвращает self.
# File activerecord/lib/active_record/persistence.rb, line 326
def toggle(attribute)
self[attribute] = !send("#{attribute}?")
self
end Обёртка над toggle, которая сохраняет запись. Этот метод отличается от своей версии без символа восклицательного знака тем, что он пропускает через него устанавливающее значение атрибута. Сохранение не подвержено проверкам валидации. Возвращает true, если запись была сохранена.
# File activerecord/lib/active_record/persistence.rb, line 335 def toggle!(attribute) toggle(attribute).update_attribute(attribute, self[attribute]) end
Сохраняет запись с обновлёнными атрибутами updated_at/on, установленным на текущее время. Обратите внимание, что проверки валидации не выполняются, и вызываются только обратные вызовы after_touch, after_commit и after_rollback. Если имя атрибута передано, этот атрибут обновляется вместе с атрибутами updated_at/on.
product.touch # updates updated_at/on product.touch(:designed_at) # updates the designed_at attribute and updated_at/on
Если используется вместе с 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 435
def touch(name = nil)
raise ActiveRecordError, "cannot touch on a new record object" unless persisted?
attributes = timestamp_attributes_for_update_in_model
attributes << name if name
unless attributes.empty?
current_time = current_time_from_proper_timezone
changes = {}
attributes.each do |column|
column = column.to_s
changes[column] = write_attribute(column, current_time)
end
changes[self.class.locking_column] = increment_lock if locking_enabled?
changed_attributes.except!(*changes.keys)
primary_key = self.class.primary_key
self.class.unscoped.where(primary_key => self[primary_key]).update_all(changes) == 1
else
true
end
end Обновляет атрибуты модели из переданного хэша и сохраняет запись, всё это в рамках транзакции. Если объект невалиден, сохранение завершится неудачно и вернёт false.
# File activerecord/lib/active_record/persistence.rb, line 228
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 241
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 Обновляет один атрибут и сохраняет запись. Это особенно полезно для булевых флагов существующих записей. Также обратите внимание, что:
-
Проверка валидации пропущена.
-
Вызываются обратные вызовы.
-
Столбец updated_at/updated_on обновляется, если такой столбец доступен.
-
Обновляются все атрибуты, которые изменены в этом объекте.
Этот метод генерирует исключение ActiveRecord::ActiveRecordError, если атрибут помечен как только для чтения.
# File activerecord/lib/active_record/persistence.rb, line 218
def update_attribute(name, value)
name = name.to_s
verify_readonly_attribute(name)
send("#{name}=", value)
save(validate: false)
end Эквивалентно update_columns(name => value).
# File activerecord/lib/active_record/persistence.rb, line 253 def update_column(name, value) update_columns(name => value) end
Обновляет атрибуты напрямую в базе данных, выполняя SQL-запрос UPDATE, и устанавливает их в получателе:
user.update_columns(last_request_at: Time.current)
Это самый быстрый способ обновления атрибутов, так как он работает напрямую с базой данных, но учтите, что стандартные процедуры обновления полностью отключаются. В частности:
-
Проверки валидации пропускаются.
-
Обратные вызовы пропускаются.
-
updated_at/updated_onне обновляются.
Этот метод генерирует исключение ActiveRecord::ActiveRecordError, когда вызывается на новых объектах или когда хотя бы один из атрибутов помечен как только для чтения.
# File activerecord/lib/active_record/persistence.rb, line 272
def update_columns(attributes)
raise ActiveRecordError, "cannot update on a new record object" unless persisted?
attributes.each_key do |key|
verify_readonly_attribute(key.to_s)
end
updated_count = self.class.unscoped.where(self.class.primary_key => id).update_all(attributes)
attributes.each do |k, v|
raw_write_attribute(k, v)
end
updated_count == 1
end
© 2004–2016 David Heinemeier Hansson
Licensed under the MIT License.