Rails find_or_create_by多个属性?

tyb*_*103 199 activerecord many-to-many model ruby-on-rails dynamic-attributes

在活动记录中有一个名为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)
Run Code Online (Sandbox Code Playgroud)

我意识到可能有更好的方法来处理这个问题,但我喜欢find_or_create这个想法的便利性.

x1a*_*1a4 461

多个属性可以与and:

GroupMember.find_or_create_by_member_id_and_group_id(4, 7)
Run Code Online (Sandbox Code Playgroud)

(find_or_initialize_by如果您不想立即保存记录,请使用)

编辑:上面的方法在Rails 4中已弃用.新的方法是:

GroupMember.where(:member_id => 4, :group_id => 7).first_or_create
Run Code Online (Sandbox Code Playgroud)

GroupMember.where(:member_id => 4, :group_id => 7).first_or_initialize
Run Code Online (Sandbox Code Playgroud)

编辑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)
Run Code Online (Sandbox Code Playgroud)

成为

GroupMember.find_or_create_by(member_id: 4, group_id: 7)
Run Code Online (Sandbox Code Playgroud)

  • .where调用也可以在rails 3中运行 (4认同)

Mar*_*rco 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
Run Code Online (Sandbox Code Playgroud)

优化提示:无论您选择哪种解决方案,请考虑为最常查询的属性添加索引.

  • 需要注意的一点是,这将不会处理无法进行质量分配的属性,而`find_or_create_by`则会处理. (8认同)
  • ```find_or_create```也有使用事务的好处,```where ... .first || create```引入了竞争条件 (3认同)

Jua*_*tas 29

在Rails 4中你可以这样做:

GroupMember.find_or_create_by(member_id: 4, group_id: 7)
Run Code Online (Sandbox Code Playgroud)

使用where方式不同:

GroupMember.where(member_id: 4, group_id: 7).first_or_create
Run Code Online (Sandbox Code Playgroud)

这将调用createGroupMember.where(member_id: 4, group_id: 7):

GroupMember.where(member_id: 4, group_id: 7).create
Run Code Online (Sandbox Code Playgroud)

相反,在find_or_create_by(member_id: 4, group_id: 7)将调用createGroupMember:

GroupMember.create(member_id: 4, group_id: 7)
Run Code Online (Sandbox Code Playgroud)

请在rails/rails上查看此相关提交.


小智 15

通过传递一个块find_or_create,您可以传递将添加到对象的其他参数(如果它是新创建的).如果要验证是否存在未搜索的字段,则此选项非常有用.

假设:

class GroupMember < ActiveRecord::Base
    validates_presence_of :name
end
Run Code Online (Sandbox Code Playgroud)

然后

GroupMember.where(:member_id => 4, :group_id => 7).first_or_create { |gm| gm.name = "John Doe" }
Run Code Online (Sandbox Code Playgroud)

将创建一个名为"John Doe"的新GroupMember,如果找不到的话 member_id 4 and group_id 7


Dor*_*ian 6

你可以做:

\n\n
User.find_or_create_by(first_name: 'Pen\xc3\xa9lope', last_name: 'Lopez')\nUser.where(first_name: 'Pen\xc3\xa9lope', last_name: 'Lopez').first_or_create\n
Run Code Online (Sandbox Code Playgroud)\n\n

或者只是初始化:

\n\n
User.find_or_initialize_by(first_name: 'Pen\xc3\xa9lope', last_name: 'Lopez')\nUser.where(first_name: 'Pen\xc3\xa9lope', last_name: 'Lopez').first_or_initialize\n
Run Code Online (Sandbox Code Playgroud)\n