206

在活动记录中有一个方便的动态属性,称为 find_or_create_by:

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

但是如果我需要通过多个属性来查找或创建呢?

假设我有一个模型来处理 Group 和 Member 之间的 M:M 关系,称为 GroupMember。我可以有很多 member_id = 4 的实例,但我不想多次遇到 member_id = 4 和 group_id = 7 的实例。我试图弄清楚是否可以做这样的事情:

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

我意识到可能有更好的方法来处理这个问题,但我喜欢 find_or_create 想法的便利性。

4

5 回答 5

476

多个属性可以用一个连接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:并非所有这些都被排除在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)
于 2010-06-15T15:31:13.410 回答
34

在 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 上查看此相关提交。

于 2014-01-04T02:37:51.953 回答
34

对于任何偶然发现此线程但需要查找或创建属性可能会根据情况发生变化的对象的任何人,请将以下方法添加到您的模型中:

# 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

优化提示:无论您选择哪种解决方案,请考虑为您最常查询的属性添加索引。

于 2012-05-26T21:00:42.927 回答
18

通过将块传递给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

于 2013-06-19T14:01:27.277 回答
5

你可以做:

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
于 2014-08-11T19:54:15.383 回答