Spec-Zone.ru › Ruby on Rails 7.1

module ActiveRecord::Persistence

Наследование Active Record

Публичные методы экземпляра

becomes(klass) Показать исходный код
# 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! вместо этого.

becomes!(klass) Показать исходный код
# 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 старого экземпляра также будет изменено, поскольку оба объекта используют один и тот же набор атрибутов.

decrement(attribute, by = 1) Показать исходный код
# File activerecord/lib/active_record/persistence.rb, line 982
def decrement(attribute, by = 1)
  increment(attribute, -by)
end

Инициализирует attribute в ноль, если nil, и вычитает значение, переданное как by (по умолчанию 1). Уменьшение выполняется непосредственно по базовому атрибуту, сеттер не вызывается. Имеет смысл только для числовых атрибутов. Возвращает self.

decrement!(attribute, by = 1, touch: nil) Показать исходный код
# 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.

delete() Показать исходный код
# 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.

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 для получения дополнительной информации.

destroy!() Показать исходный код
# 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 для получения дополнительной информации.

destroyed?() Показать исходный код
# File activerecord/lib/active_record/persistence.rb, line 682
def destroyed?
  @destroyed
end

Возвращает true, если этот объект был уничтожен, в противном случае возвращает false.

increment(attribute, by = 1) Показать исходный код
# 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.

increment!(attribute, by = 1, touch: nil) Показать исходный код
# 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.

new_record?() Показать исходный код
# File activerecord/lib/active_record/persistence.rb, line 665
def new_record?
  @new_record
end

Возвращает true, если этот объект ещё не сохранён – то есть запись для объекта ещё не существует в базе данных; в противном случае возвращает false.

persisted?() Показать исходный код
# File activerecord/lib/active_record/persistence.rb, line 688
def persisted?
  !(@new_record || @destroyed)
end

Возвращает true, если запись сохранена, т.е. это не новая запись и она не была уничтожена, в противном случае возвращает false.

previously_new_record?() Показать исходный код
# File activerecord/lib/active_record/persistence.rb, line 672
def previously_new_record?
  @previously_new_record
end

Возвращает true, если этот объект только что был создан – то есть до последнего обновления или удаления объект не существовал в базе данных, и new_record? вернул бы true.

previously_persisted?() Показать исходный код
# File activerecord/lib/active_record/persistence.rb, line 677
def previously_persisted?
  !new_record? && destroyed?
end

Возвращает true, если этот объект был ранее сохранен, но теперь он был удален.

reload(options = nil) Show source
# 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
save(**options) Show source
# 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, помеченные как доступные только для чтения, игнорируются, если запись обновляется.

save!(**options) Show source
# 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.

toggle(attribute) Show source
# 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
toggle!(attribute) Show source
# File activerecord/lib/active_record/persistence.rb, line 1017
def toggle!(attribute)
  toggle(attribute).update_attribute(attribute, self[attribute])
end

Обёртка вокруг toggle, которая сохраняет запись. Этот метод отличается от своей не-bang версии тем, что он проходит через установщик атрибута. Сохранение не подвергается проверкам на валидность. Возвращает true, если запись может быть сохранена.

touch(*names, time: nil) Show source
# 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
update(attributes) Show source
# 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.

update!(attributes) Show source
# 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, поэтому генерируется исключение, если запись неверна и сохранение завершится неудачей.

update_attribute(name, value) Show source
# 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.

update_attribute!(name, value) Show source
# 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 для получения более подробной информации.

update_column(name, value) Show source
# File activerecord/lib/active_record/persistence.rb, line 911
def update_column(name, value)
  update_columns(name => value)
end

Эквивалентно update_columns(name => value).

update_columns(attributes) Показать исходный код
# 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.

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API