module ActiveRecord::Persistence
Наследование Active Record
Открытые методы экземпляра
# File activerecord/lib/active_record/persistence.rb, line 487
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 вместо clients/client.
Примечание: новый экземпляр будет совместно использовать ссылку на те же атрибуты, что и исходный класс. Поэтому значение столбца STI останется тем же. Любое изменение атрибутов в любом из экземпляров повлияет на оба экземпляра. Это включает в себя любую инициализацию атрибутов, выполненную новым экземпляром.
Если вы также хотите изменить столбец STI, используйте becomes! вместо этого.
# File activerecord/lib/active_record/persistence.rb, line 508
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 655 def decrement(attribute, by = 1) increment(attribute, -by) end
Инициализирует attribute в ноль, если nil, и вычитает значение, переданное как by (по умолчанию 1). Уменьшение выполняется непосредственно на базовом атрибуте, сеттер не вызывается. Имеет смысл только для числовых атрибутов. Возвращает self.
# File activerecord/lib/active_record/persistence.rb, line 665 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 439 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 453 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 469 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 355 def destroyed? @destroyed end
Возвращает true, если этот объект был уничтожен, в противном случае возвращает false.
# File activerecord/lib/active_record/persistence.rb, line 632 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 644
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 338 def new_record? @new_record end
Возвращает true, если этот объект еще не сохранен — то есть запись для объекта еще не существует в базе данных; в противном случае возвращает false.
# File activerecord/lib/active_record/persistence.rb, line 361 def persisted? !(@new_record || @destroyed) end
Возвращает true, если запись сохранена, т.е. это не новая запись и она не была уничтожена, в противном случае возвращает false.
# File activerecord/lib/active_record/persistence.rb, line 345 def previously_new_record? @previously_new_record end
Возвращает true, если этот объект только что был создан — то есть до последнего обновления или удаления объект не существовал в базе данных, и new_record? вернул бы true.
# File activerecord/lib/active_record/persistence.rb, line 350 def previously_persisted? !new_record? && destroyed? end
Возвращает true, если этот объект был ранее сохранен, но теперь он был удален.
# File activerecord/lib/active_record/persistence.rb, line 742
def reload(options = nil)
self.class.connection_pool.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)
@association_cache.each_value { |association| association.owner = self }
@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 390 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 423
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 681
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 690 def toggle!(attribute) toggle(attribute).update_attribute(attribute, self[attribute]) end
Обёртка вокруг toggle, которая сохраняет запись. Этот метод отличается от своей версии без «!» тем, что он проходит через сеттер атрибута. Сохранение не подвергается проверкам валидации. Возвращает true, если запись может быть сохранена.
# File activerecord/lib/active_record/persistence.rb, line 793
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 563
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 574
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 530
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 552
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 584 def update_column(name, value) update_columns(name => value) end
Эквивалентно update_columns(name => value).
# File activerecord/lib/active_record/persistence.rb, line 604
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.