Rails创build或更新魔法?

我有一个名为CachedObject的类,它存储了通过键索引的generics序列化对象。 我想要这个类来实现一个create_or_update方法。 如果find一个对象,它将更新它,否则它将创build一个新的。

有没有办法在Rails中做到这一点,或者我必须写我自己的方法?

如果您正在查找“upsert”(在相同的操作中数据库执行更新或插入语句)types的语句。 开箱即用,Rails和ActiveRecord没有这样的function。 但是,您可以使用upsertgem。

否则,可以使用: find_or_initialize_byfind_or_create_by ,它们提供了类似的function,尽pipe这是以额外的数据库命中为代价的,在大多数情况下,这几乎不是问题。 所以除非你有严重的性能问题,否则我不会使用gem。

例如,如果找不到名称为“Roger”的用户,则将其name设置为“Roger”的新用户实例实例化。

 user = User.where(name: "Roger").first_or_initialize user.email = "email@example.com" user.save 

或者,您可以使用find_or_initialize_by

 user = User.find_or_initialize_by(name: "Roger") 

在Rails 3中。

 user = User.find_or_initialize_by_name("Roger") user.email = "email@example.com" user.save 

您可以使用块,但块只在logging是新的时才运行

 User.where(name: "Roger").first_or_initialize do |user| # this won't run if a user with name "Roger" is found user.save end User.find_or_initialize_by(name: "Roger") do |user| user.save end 

如果要使用块而不考虑logging的持久性,请tap结果:

 User.where(name: "Roger").first_or_initialize.tap do |user| user.email = "email@example.com" user.save end 

在Rails 4中,您可以添加到特定的模型:

 def self.update_or_create(attributes) assign_or_new(attributes).save end def self.assign_or_new(attributes) obj = first || new obj.assign_attributes(attributes) obj end 

并使用它

 User.where(email: "a@b.com").update_or_create(name: "Mr A Bbb") 

或者,如果您希望将这些方法添加到放入初始化程序的所有模型中:

 module ActiveRecordExtras module Relation extend ActiveSupport::Concern module ClassMethods def update_or_create(attributes) assign_or_new(attributes).save end def update_or_create!(attributes) assign_or_new(attributes).save! end def assign_or_new(attributes) obj = first || new obj.assign_attributes(attributes) obj end end end end ActiveRecord::Base.send :include, ActiveRecordExtras::Relation 

将此添加到您的模型中:

 def self.update_or_create_by(args, attributes) obj = self.find_or_create_by(args) obj.update(attributes) return obj end 

有了这个,你可以:

 User.update_or_create_by({name: 'Joe'}, attributes) 

你可以这样做:

 CachedObject.where(key: "the given key").first_or_create! do |cached| cached.attribute1 = 'attribute value' cached.attribute2 = 'attribute value' end 

老问题,但把我的解决scheme,以完整性的环。 我需要这个,当我需要一个具体的发现,但不同的创build,如果它不存在。

 def self.find_by_or_create_with(args, attributes) # READ CAREFULLY! args for finding, attributes for creating! obj = self.find_or_initialize_by(args) return obj if obj.persisted? return obj if obj.update_attributes(attributes) end 

续集 gem增加了一个update_or_create方法,这似乎是做你在找什么。