Rails的find_or_create由多个属性?

活动logging中有一个方便的dynamic属性叫做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的方便。

一个and可以连接多个属性:

 GroupMember.find_or_create_by_member_id_and_group_id(4, 7) 

(如果您不想立即保存logging,请使用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) 

对于任何人在这个线程中绊倒,但需要find或创build一个对象的属性可能会根据情况而改变,添加以下方法到您的模型:

 # 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 

优化提示:无论您select哪种解决scheme,都要考虑为最频繁查询的属性添加索引。

在Rails 4中你可以这样做:

 GroupMember.find_or_create_by(member_id: 4, group_id: 7) 

和使用不同的地方:

 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)将在find_or_create_by(member_id: 4, group_id: 7)调用create

 GroupMember.create(member_id: 4, group_id: 7) 

请在rails / rails上看到这个相关的提交 。

通过传递一个块给find_or_create ,你可以传递额外的参数,如果它是新build的,将被添加到对象中。 如果您正在validation您没有search的字段的存在,这非常有用。

假设:

 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" } 

将会创build一个名为“John Doe”的新GroupMe,如果它没有findmember_id 4 and group_id 7

你可以做:

 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