キーでインデックス付けされたジェネリックシリアル化オブジェクトを格納するCachedObject
というクラスがあります。このクラスにcreate_or_update
メソッドを実装してほしい。オブジェクトが見つかるとそれを更新し、見つからない場合は新しいオブジェクトを作成します。
Railsでこれを行う方法はありますか、または独自のメソッドを記述する必要がありますか?
「upsert」(データベースが同じ操作でupdateまたはinsertステートメントを実行する)タイプのステートメントを探している場合は、そうではありません。初期状態では、RailsとActiveRecordにはそのような機能はありません。ただし、 upsert gemを使用できます。
それ以外の場合は、 find_or_initialize_by
または find_or_create_by
を使用できます。これらは、追加のデータベースヒットが発生しますが、 、ほとんどの場合、ほとんど問題になりません。したがって、パフォーマンスに関する深刻な懸念がない限り、このgemは使用しません。
たとえば、「Roger」という名前のユーザーが見つからない場合、name
が「Roger」に設定された新しいユーザーインスタンスがインスタンス化されます。
user = User.where(name: "Roger").first_or_initialize
user.email = "[email protected]"
user.save
または、find_or_initialize_by
を使用できます。
user = User.find_or_initialize_by(name: "Roger")
Rails 3。
user = User.find_or_initialize_by_name("Roger")
user.email = "[email protected]"
user.save
ブロックを使用できますが、ブロックが実行されるのは、レコードが新しい場合のみです。
User.where(name: "Roger").first_or_initialize do |user|
# this won't run if a user with name "Roger" is found
user.save
end
User.find_or_initialize_by(name: "Roger") do |user|
# this also won't run if a user with name "Roger" is found
user.save
end
レコードの永続性に関係なくブロックを使用する場合は、結果に対して tap
を使用します。
User.where(name: "Roger").first_or_initialize.tap do |user|
user.email = "[email protected]"
user.save
end
Rails 4では、特定のモデルに追加できます:
def self.update_or_create(attributes)
assign_or_new(attributes).save
end
def self.assign_or_new(attributes)
obj = first || new
obj.assign_attributes(attributes)
obj
end
そしてそれを次のように使用します
User.where(email: "[email protected]").update_or_create(name: "Mr A Bbb")
または、これらのメソッドを初期化子に配置されたすべてのモデルに追加する場合:
module ActiveRecordExtras
module Relation
extend ActiveSupport::Concern
module ClassMethods
def update_or_create(attributes)
assign_or_new(attributes).save
end
def update_or_create!(attributes)
assign_or_new(attributes).save!
end
def assign_or_new(attributes)
obj = first || new
obj.assign_attributes(attributes)
obj
end
end
end
end
ActiveRecord::Base.send :include, ActiveRecordExtras::Relation
これをモデルに追加します。
def self.update_or_create_by(args, attributes)
obj = self.find_or_create_by(args)
obj.update(attributes)
return obj
end
それにより、次のことができます。
User.update_or_create_by({name: 'Joe'}, attributes)
探していた魔法がRails 6
に追加されました。これで、アップサート(更新または挿入)できます。
Model.upsert(column_name: value)
注:一括更新または一括作成には、upsert_all
を使用できます
次のような1つのステートメントで実行できます。
CachedObject.where(key: "the given key").first_or_create! do |cached|
cached.attribute1 = 'attribute value'
cached.attribute2 = 'attribute value'
end
古い質問ですが、完全を期すために私のソリューションをリングに投げ込みました。特定の検索が必要なときにこれが必要でしたが、存在しない場合は別の作成が必要でした。
def self.find_by_or_create_with(args, attributes) # READ CAREFULLY! args for finding, attributes for creating!
obj = self.find_or_initialize_by(args)
return obj if obj.persisted?
return obj if obj.update_attributes(attributes)
end
sequel gemは pdate_or_create メソッドを追加します。これはあなたが探していることをするようです。