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 多个属性?的主要内容,如果未能解决你的问题,请参考以下文章

Rails 在幂等种子文件中查找或创建 - 不通过验证

Rails - 过滤多对多

Rails 6.1 多数据库外键

带有文件上传的 Rails 3 多步骤表单

与多态表的一对多条件关联:Rails

当文本字段填充多对多表单时,Rails 3.2 Ajax 更新 Div