Rails find_or_create_by多个属性?

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

Model.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思想的便利性。

115360 次浏览

多个属性可以连接到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

并不是所有这些都被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)

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

# 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

优化建议:不管你选择哪种解决方案,都要考虑为你最频繁查询的属性添加索引。

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

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

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

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

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

你可以:

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