Rails的find_or_create_by不止一个属性?


202

活动记录中有一个方便的动态属性,称为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的方便性。

Answers:


464

多个属性可以与连接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:并非所有这些因素都只是特定于属性的因素。

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)

您可能还喜欢github.com/seamusabshere/upsert-第一个参数是用于查找或创建记录的属性的哈希
Seamus Abshere

1
请注意,初始化调用的是create()而不是在first_or_create情况下可能会调用的new()
Sumit Bisht

有人可以分享这种用法的要点吗?我对它的放置和调用不熟悉。
6英尺丹

我恢复了删除Rails 3代码的方法,因为很多人还没有使用Rails 4。
Kyle Heironimus 2014年

只是要添加:在Rails 4中不推荐使用find_or_create_by _ *()函数,但是,不推荐使用find_or_create_by(*)函数。可以使用find_or_create_by()。检查提交本地方加入github.com/rails/rails/commit/...和官方的Rails的使用4.2文件:guides.rubyonrails.org/v4.2.0/...
CodeExpress

33

对于偶然发现此线程但需要查找或创建其属性可能随情况而变化的对象的其他任何人,请在模型中添加以下方法:

# 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

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


8
需要注意的一件事是,这不会处理无法批量分配的属性,而find_or_create_by会处理。
x1a4

1
Marco,尝试Model.where(attributes).instance_eval{|q| q.first || q.create}
hiroshi 2012年

3
find_or_create还具有使用事务的好处,它where….first || create引入了竞争条件
mrm

我要说的 def self.find_or_create(attributes) self.where(attributes).first || self.create(attributes) end是,您无需重复Model
Augustin Riedinger

@AugustinRiedinger您也不需要self在方法主体内部(因为您要删除单词!)。
David Tuite 2014年

31

在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

这将调用createGroupMember.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上查看此相关提交


16

通过传递一个块到 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”创建一个新的GroupMember member_id 4 and group_id 7


4

你可以做:

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.