活动记录中有一个名为find_or_create_by的便捷动态属性:
Model.find_or_create_by_<attribute>(:<attribute> => "")
但是如果我需要通过多个属性find_or_create呢?
假设我有一个模型来处理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这个想法的便利。
答案 0 :(得分:457)
可以使用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)
答案 1 :(得分:32)
对于偶然发现此线程但需要查找或创建具有可能根据具体情况而变化的属性的对象的其他人,请将以下方法添加到模型中:
# 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
优化提示,无论您选择哪种解决方案,请考虑为您最常查询的属性添加索引。
答案 2 :(得分:27)
在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)
会在create
上致电GroupMember
:
GroupMember.create(member_id: 4, group_id: 7)
请在rails / rails上查看相关的commit。
答案 3 :(得分:14)
通过将块传递给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
答案 4 :(得分: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