Rails创build或更新魔法?
我有一个名为CachedObject
的类,它存储了通过键索引的generics序列化对象。 我想要这个类来实现一个create_or_update
方法。 如果find一个对象,它将更新它,否则它将创build一个新的。
有没有办法在Rails中做到这一点,或者我必须写我自己的方法?
如果您正在查找“upsert”(在相同的操作中数据库执行更新或插入语句)types的语句。 开箱即用,Rails和ActiveRecord没有这样的function。 但是,您可以使用upsertgem。
否则,可以使用: find_or_initialize_by
或find_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方法,这似乎是做你在找什么。