在活动记录中有一个方便的动态属性,称为 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 想法的便利性。
多个属性可以用 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)
对于任何偶然发现此线程但需要查找或创建属性可能会根据情况发生变化的对象的任何人,请将以下方法添加到您的模型中:
# 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_by
会。
Model.where(attributes).instance_eval{|q| q.first || q.create}
。
find_or_create
还具有使用事务的好处,其中 where….first || create
引入了竞争条件
def self.find_or_create(attributes) self.where(attributes).first || self.create(attributes) end
,这样您就不需要重复Model
self
在方法体内(因为您要删除单词!)。
在 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)
请在 rails/rails 上查看相关的 commit。
通过将块传递给 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 4
and group_id 7
的 GroupMember,它将创建一个名为“John Doe”的新 GroupMember
你可以做:
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