web-dev-qa-db-ja.com

Rails find_or_create_by複数の属性?

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

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

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

GroupMemberというグループとメンバー間のM:M関係を処理するモデルがあるとします。 member_id = 4のインスタンスを多数持つことができますが、member_id = 4およびgroup_id = 7のインスタンスを複数回使用することは望ましくありません。

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

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

197
tybro0103

複数の属性をandで接続できます:

GroupMember.find_or_create_by_member_id_and_group_id(4, 7)

(すぐにレコードを保存したくない場合は、find_or_initialize_byを使用してください)

Edit:上記のメソッドは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:これらのすべてがRails属性固有のものだけから除外されたわけではありません。

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)
458
x1a4

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

# 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

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

32
Marco

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

これにより、GroupMember.where(member_id: 4, group_id: 7)createが呼び出されます。

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)

この関連する commit をRails/railsで参照してください。

28
Juanito Fatas

ブロックを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" }

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

15
Daniel Murphy

できるよ:

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
4
Dorian