Rails find_or_create_by複数の属性?


202

find_or_create_byというアクティブレコードには、便利な動的属性があります。

Model.find_or_create_by_<attribute>(:<attribute> => "")

しかし、複数の属性でfind_or_createを実行する必要がある場合はどうなりますか?

GroupとMemberの間のGroupMemberと呼ばれるM:M関係を処理するモデルがあるとします。member_id = 4のインスタンスを多数持つことができますが、member_id = 4とgroup_id = 7のインスタンスを2回以上使いたくありません。

GroupMember.find_or_create(:member_id => 4, :group_id => 7)

これを処理するより良い方法があるかもしれないと思いますが、find_or_createのアイデアの便利さを気に入っています。

回答:


464

複数の属性をand

GroupMember.find_or_create_by_member_id_and_group_id(4, 7)

find_or_initialize_byすぐにレコードを保存したくない場合に使用)

編集:上記のメソッドはRails 4では非推奨です。新しい方法はのとおりです。

GroupMember.where(:member_id => 4, :group_id => 7).first_or_create

そして

GroupMember.where(:member_id => 4, :group_id => 7).first_or_initialize

編集2:これらすべてが属性固有のものだけでレールから除外されたわけではありません。

https://github.com/rails/rails/blob/4-2-stable/guides/source/active_record_querying.md

GroupMember.find_or_create_by_member_id_and_group_id(4, 7)

なりました

GroupMember.find_or_create_by(member_id: 4, group_id: 7)

github.com/seamusabshere/upsertもお勧めです。最初の引数は、レコードの検索または作成に使用される属性のハッシュです
Seamus Abshere

1
初期化を呼び出すことに注意することは有益である作成()の代わりにfirst_or_createの場合に呼ばれるかもしれないこと)(新しいの
スミットBisht

誰かがこの使用法の要旨を共有できますか?私はその配置と呼び出しに精通していません。
6フィートダン2014

多くの人がまだRails 4を使用していないため、Rails 3コードの削除を元に戻しました。
Kyle Heironimus 14

追加するだけです:find_or_create_by _ *()関数はRails 4で非推奨になりましたが、find_or_create_by()は非推奨です。find_or_create_by()を使用しても問題ありません。チェックこれを添加したところコミットgithub.com/rails/rails/commit/...と使用のための公式のRails 4.2のドキュメント:guides.rubyonrails.org/v4.2.0/...
CodeExpress

33

このスレッドに出くわしたが、状況に応じて変化する可能性のある属性を持つオブジェクトを検索または作成する必要がある他の人のために、次のメソッドをモデルに追加します。

# Return the first object which matches the attributes hash
# - or -
# Create new object with the given attributes
#
def self.find_or_create(attributes)
  Model.where(attributes).first || Model.create(attributes)
end

最適化のヒント:選択するソリューションに関係なく、最も頻繁に照会する属性のインデックスを追加することを検討してください。


8
注意すべきことの1つは、これは一括割り当てできない属性を処理しないことですfind_or_create_by
x1a4 2012

1
マルコ、試してみてくださいModel.where(attributes).instance_eval{|q| q.first || q.create}
hiroshi

3
find_or_createwhere….first || create競合状態が発生するトランザクションを使用する利点もあります
mrm

def self.find_or_create(attributes) self.where(attributes).first || self.create(attributes) end繰り返す必要がないように言い ますModel
オーガスティンリーディンガー2013

@AugustinRiedinger selfメソッド本体の内部も必要ありません(単語を削除しようとしているためです!)。
David Tuite、2014

31

Rails 4では次のことができます:

GroupMember.find_or_create_by(member_id: 4, group_id: 7)

そして使用方法whereは異なります:

GroupMember.where(member_id: 4, group_id: 7).first_or_create

これが必要createになりますGroupMember.where(member_id: 4, group_id: 7)

GroupMember.where(member_id: 4, group_id: 7).create

逆に、find_or_create_by(member_id: 4, group_id: 7)呼び出しますcreateGroupMember

GroupMember.create(member_id: 4, group_id: 7)

rails / railsでこの関連コミットを参照してください。


16

にブロックを渡すことにより find_or_createに、オブジェクトが新しく作成された場合にオブジェクトに追加される追加のパラメーターを渡すことができます。これは、検索していないフィールドの存在を検証する場合に役立ちます。

仮定:

class GroupMember < ActiveRecord::Base
    validates_presence_of :name
end

その後

GroupMember.where(:member_id => 4, :group_id => 7).first_or_create { |gm| gm.name = "John Doe" }

が見つからない場合は、「John Doe」という名前の新しいGroupMemberを作成します。 member_id 4 and group_id 7


4

できるよ:

User.find_or_create_by(first_name: 'Penélope', last_name: 'Lopez')
User.where(first_name: 'Penélope', last_name: 'Lopez').first_or_create

または単に初期化するには:

User.find_or_initialize_by(first_name: 'Penélope', last_name: 'Lopez')
User.where(first_name: 'Penélope', last_name: 'Lopez').first_or_initialize

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.