active-record中有一个方便的动态属性find_or_create_by:

模型。Find_or_create_by_ <attribute>(:<attribute> => "")

但是,如果我需要通过多个属性查找find_or_create,该怎么办呢?

假设我有一个模型来处理组和成员之间的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思想的便利性。


当前回答

你可以:

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

其他回答

通过将块传递给find_or_create,您可以传递额外的参数,这些参数将在创建新对象时添加到对象中。如果您正在验证某个字段的存在,而不是通过该字段进行搜索,那么这是非常有用的。

假设:

class GroupMember < ActiveRecord::Base
    validates_presence_of :name
end

then

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

将创建一个新的GroupMember与名称“John Doe”,如果它没有找到一个与member_id 4和group_id 7

对于无意中发现这个线程,但需要找到或创建一个属性可能会根据环境而改变的对象的其他人,请将以下方法添加到您的模型中:

# 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

优化技巧:无论选择哪种解决方案,都要考虑为最频繁查询的属性添加索引。

在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上的create。(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)将在GroupMember上调用create:

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

请参见此相关提交rails/rails。

多个属性可以用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

and

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)

你可以:

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