110

CachedObjectキーによってインデックス付けされた一般的なシリアル化されたオブジェクトを格納するというクラスがあります。このクラスにメソッドを実装させたいcreate_or_update。オブジェクトが見つかった場合はそれを更新し、そうでない場合は新しいオブジェクトを作成します。

Rails でこれを行う方法はありますか、それとも独自のメソッドを作成する必要がありますか?

4

7 に答える 7

203

レール6

Rails 6 では、この機能を提供するupsertandupsert_allメソッドが追加されました。

Model.upsert(column_name: value)

[アップサート] モデルをインスタンス化することも、Active Record コールバックや検証をトリガーすることもありません。

レール 5、4、および 3

「アップサート」(データベースが同じ操作で更新または挿入ステートメントを実行する)タイプのステートメントを探している場合はそうではありません。Rails と ActiveRecord には、すぐに使用できる機能はありません。ただし、 upsert gemを使用することはできます。

それ以外の場合は、 :find_or_initialize_byまたはを使用できます。find_or_create_byこれらは同様の機能を提供しますが、追加のデータベース ヒットが発生しますが、ほとんどの場合、ほとんど問題にはなりません。したがって、重大なパフォーマンス上の問題がない限り、私はこの gem を使用しません。

たとえば、「Roger」という名前のユーザーが見つからない場合、「Roger」にname設定された新しいユーザー インスタンスがインスタンス化されます。

user = User.where(name: "Roger").first_or_initialize
user.email = "email@example.com"
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@example.com"
user.save

ブロックを使用できますが、ブロックはレコードが new の場合にのみ実行されます。

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@example.com"
  user.save
end
于 2013-09-11T17:01:11.677 に答える
34

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: "a@b.com").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
于 2014-10-07T16:32:07.680 に答える
13

これをモデルに追加します。

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)
于 2014-05-16T22:47:52.477 に答える
1

古い質問ですが、完全を期すために私の解決策をリングに投げ込みます。特定の検索が必要なときにこれが必要でしたが、存在しない場合は別の作成が必要でした。

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
于 2016-08-30T00:14:03.283 に答える
0

続編の宝石は、探していることを行うように見えるupdate_or_createメソッドを追加します。

于 2014-05-28T16:04:04.380 に答える