跳到內容 跳到搜尋

Active Record Persistence

命名空間
方法
B
D
I
N
P
R
S
T
U

實例公開方法

becomes(klass)

傳回一個指定 klass 的實例,並具有目前記錄的屬性。這在單一資料表繼承 (STI) 結構中特別有用,在這種結構中,您希望子類別顯示為超類別。這可以與 Action Pack 中的記錄識別一起使用,例如,允許 Client < Company 執行類似於 partial: @client.becomes(Company) 的動作,以使用 companies/company 部分,而非 clients/client,來呈現該實例。

注意:新的實例將與原始類別共享連結至相同屬性的連結。因此,STI 欄位值仍然相同。對任一實例屬性的任何變更都會影響兩個實例。這包括新實例執行的任何屬性初始化。

如果您也想要變更 STI 欄位,請改用 becomes!

# 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

becomes!(klass)

becomes 的包裝器,它也會變更實例的 STI 欄位值。如果您想要在資料庫中保留已變更的類別,這會特別有用。

注意:舊實例的 STI 欄位值也會變更,因為這兩個物件共用同一組屬性。

# 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

decrement(屬性, 由 = 1)

如果為 nil,將 屬性 初始化為零,並減去傳遞為 by 的值(預設為 1)。減法會直接在基礎屬性上執行,不會呼叫設定器。僅對數字型屬性有意義。傳回 self

# File activerecord/lib/active_record/persistence.rb, line 982
def decrement(attribute, by = 1)
  increment(attribute, -by)
end

decrement!(屬性, 由 = 1, 觸發: nil)

包覆在 decrement 中的包裝器,會將更新寫入資料庫。僅更新 屬性;不會儲存記錄本身。這表示任何其他已修改的屬性仍會是變更過的。會略過 驗證 和回呼。支援來自 update_counters觸發 選項,請參閱以取得更多資訊。傳回 self

# File activerecord/lib/active_record/persistence.rb, line 992
def decrement!(attribute, by = 1, touch: nil)
  increment!(attribute, -by, touch: touch)
end

delete()

刪除資料庫中的記錄,並凍結此實例以反映不應進行任何變更(因為無法持續)。傳回凍結的實例。

這列會使用記錄主鍵的 SQL DELETE 陳述式單純移除,且不會執行任何回呼。

請注意,這也會刪除標記為 #readonly? 的記錄。

若要強制執行物件的 before_destroyafter_destroy 回呼或任何 :dependent 關聯選項,請使用 destroy

# File activerecord/lib/active_record/persistence.rb, line 766
def delete
  _delete_row if persisted?
  @destroyed = true
  @previously_new_record = false
  freeze
end

destroy()

刪除資料庫中的記錄,並凍結此實例以反映不應進行任何變更(因為它們無法持續存在)。

destroy 關聯有一系列的回呼。如果 before_destroy 回呼擲回 :abort,則動作會取消,而 destroy 會傳回 false。請參閱 ActiveRecord::Callbacks 以取得進一步的詳細資訊。

# 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!()

刪除資料庫中的記錄,並凍結此實例以反映不應進行任何變更(因為它們無法持續存在)。

destroy! 關聯有一系列的回呼。如果 before_destroy 回呼擲回 :abort,則動作會取消,而 destroy! 會引發 ActiveRecord::RecordNotDestroyed。請參閱 ActiveRecord::Callbacks 以取得進一步的詳細資訊。

# File activerecord/lib/active_record/persistence.rb, line 796
def destroy!
  destroy || _raise_record_not_destroyed
end

destroyed?()

如果此物件已被銷毀,則傳回 true,否則傳回 false。

# File activerecord/lib/active_record/persistence.rb, line 682
def destroyed?
  @destroyed
end

increment(attribute, by = 1)

如果 attributenil,則將其初始化為零,並加上傳遞為 by 的值(預設為 1)。增量直接在基礎屬性上執行,不會呼叫設定器。僅對基於數字的屬性有意義。傳回 self

# File activerecord/lib/active_record/persistence.rb, line 959
def increment(attribute, by = 1)
  self[attribute] ||= 0
  self[attribute] += by
  self
end

increment!(attribute, by = 1, touch: nil)

包覆 increment 的包裝器,將更新寫入資料庫。只有 attribute 會更新;記錄本身不會儲存。這表示任何其他已修改的屬性仍會是骯髒的。會略過 Validations 和回呼。支援來自 update_counterstouch 選項,請參閱以取得更多資訊。傳回 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

new_record?()

如果這個物件尚未儲存,則傳回 true,也就是說,資料庫中尚未有這個物件的紀錄;否則,傳回 false。

# File activerecord/lib/active_record/persistence.rb, line 665
def new_record?
  @new_record
end

persisted?()

如果紀錄已持久化,則傳回 true,也就是說,它不是新紀錄,而且尚未被銷毀,否則傳回 false。

# File activerecord/lib/active_record/persistence.rb, line 688
def persisted?
  !(@new_record || @destroyed)
end

previously_new_record?()

如果這個物件剛剛建立,則傳回 true,也就是說,在上次更新或刪除之前,這個物件不存在於資料庫中,而且 new_record? 會傳回 true。

# File activerecord/lib/active_record/persistence.rb, line 672
def previously_new_record?
  @previously_new_record
end

previously_persisted?()

如果這個物件先前已持久化,但現在已刪除,則傳回 true。

# File activerecord/lib/active_record/persistence.rb, line 677
def previously_persisted?
  !new_record? && destroyed?
end

reload(options = nil)

從資料庫重新載入紀錄。

這個方法會透過主鍵(可以手動指定)尋找紀錄,並直接修改接收者

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: '[email protected]'>

屬性會從資料庫重新載入,並清除快取,特別是關聯快取和 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 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)
  @association_cache.each_value { |association| association.owner = self }
  @attributes = fresh_object.instance_variable_get(:@attributes)
  @new_record = false
  @previously_new_record = false
  self
end

save(**options)

儲存模型。

如果模型是新的,則會在資料庫中建立記錄,否則會更新現有記錄。

預設情況下,儲存總是執行驗證。如果其中任何一個驗證失敗,則會取消動作,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 717
def save(**options, &block)
  create_or_update(**options, &block)
rescue ActiveRecord::RecordInvalid
  false
end

save!(**options)

儲存模型。

如果模型是新的,則會在資料庫中建立記錄,否則會更新現有記錄。

預設情況下,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 750
def save!(**options, &block)
  create_or_update(**options, &block) || raise(RecordNotSaved.new("Failed to save the record", self))
end

toggle(attribute)

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 1008
def toggle(attribute)
  self[attribute] = !public_send("#{attribute}?")
  self
end

toggle!(attribute)

包覆在 toggle 中,用於儲存記錄。此方法與其非驚嘆號版本不同之處在於,它會傳遞屬性設定器。儲存不受驗證檢查的約束。如果可以儲存記錄,則傳回 true

# File activerecord/lib/active_record/persistence.rb, line 1017
def toggle!(attribute)
  toggle(attribute).update_attribute(attribute, self[attribute])
end

touch(*names, time: nil)

將記錄儲存為 updated_at/on 屬性設定為目前時間或指定的時間。請注意,不會執行任何驗證,而且只會執行 after_touchafter_commitafter_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 1120
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

update(attributes)

從傳入的雜湊更新模型的屬性,並儲存記錄,全部包在一個交易中。如果物件無效,儲存將會失敗,並傳回 false。

# 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

update!(attributes)

更新接收器,就像 update 一樣,但呼叫 save! 而不是 save,因此如果記錄無效且儲存將會失敗,則會引發例外狀況。

# 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_attribute(name, value)

更新單一屬性並儲存記錄。這對於現有記錄上的布林旗標特別有用。另外請注意

  • 驗證會被略過。

  • 會呼叫回呼。

  • 如果 updated_at/updated_on 欄位可用,則會更新。

  • 更新此物件中所有已變更的屬性。

如果屬性標記為唯讀,此方法會引發 ActiveRecord::ActiveRecordError

另請參閱 update_column

# 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

update_attribute!(name, value)

更新單一屬性並儲存記錄。這對於現有記錄上的布林旗標特別有用。另外請注意

  • 驗證會被略過。

  • 會呼叫回呼。

  • 如果 updated_at/updated_on 欄位可用,則會更新。

  • 更新此物件中所有已變更的屬性。

如果屬性標記為唯讀,此方法會引發 ActiveRecord::ActiveRecordError

如果任何 before_* 回呼擲回 :abort,則動作會被取消,而 update_attribute! 會引發 ActiveRecord::RecordNotSaved。請參閱 ActiveRecord::Callbacks 以取得進一步的詳細資訊。

# 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

update_column(name, value)

等同於 update_columns(name => value)

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

update_columns(attributes)

直接在資料庫中更新屬性,發出 UPDATE SQL 陳述式,並將其設定在接收者中

user.update_columns(last_request_at: Time.current)

這是更新屬性最快的途徑,因為它直接進入資料庫,但請注意,因此常規更新程序會完全被略過。特別是

  • 驗證會被略過。

  • 回呼會被略過。

  • updated_at/updated_on 沒有更新。

  • 不過,屬性會使用與 ActiveRecord::Relation#update_all 相同的規則序列化

當在新的物件上呼叫此方法,或至少一個屬性標記為唯讀時,此方法會引發 ActiveRecord::ActiveRecordError

# 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