Rails find_or_create_by 多个属性?
Posted
技术标签:
【中文标题】Rails find_or_create_by 多个属性?【英文标题】:Rails find_or_create_by more than one attribute? 【发布时间】:2011-03-04 01:28:24 【问题描述】:在活动记录中有一个方便的动态属性,叫做 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 想法的便利性。
【问题讨论】:
【参考方案1】:多个属性可以用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)
【讨论】:
您可能还喜欢github.com/seamusabshere/upsert - 第一个参数是用于查找或创建记录的属性哈希 请注意,initialize 调用 create() 而不是在 first_or_create 的情况下可能会调用的 new() 有人可以分享这个用法的要点示例吗?我不熟悉它的位置和调用。 我恢复了对 Rails 3 代码的删除,因为很多人还没有使用 Rails 4。 补充一点:find_or_create_by_*() 函数在 Rails 4 中已被弃用,但 find_or_create_by() 不是。可以使用 find_or_create_by()。检查添加 github.com/rails/rails/commit/… 的提交和官方 Rails 4.2 文档的用法:guides.rubyonrails.org/v4.2.0/…【参考方案2】:对于任何偶然发现此线程但需要查找或创建属性可能会根据情况发生变化的对象的其他人,请将以下方法添加到您的模型中:
# 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
@AugustinRiedinger 您也不需要在方法主体中使用self
(因为您要删除单词!)。【参考方案3】:
通过将块传递给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
【讨论】:
【参考方案4】:在 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。
【讨论】:
【参考方案5】:你可以这样做:
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
【讨论】:
api.rubyonrails.org/classes/ActiveRecord/…以上是关于Rails find_or_create_by 多个属性?的主要内容,如果未能解决你的问题,请参考以下文章