Les rails créent ou mettent à jour la magie?

J’ai une classe appelée CachedObject qui stocke les objects sérialisés génériques indexés par clé. Je veux que cette classe implémente une méthode create_or_update . Si un object est trouvé, il le mettra à jour, sinon il en créera un nouveau.

Est-il possible de le faire dans Rails ou dois-je écrire ma propre méthode?

Pas si vous cherchez un “upsert” (où la firebase database exécute une mise à jour ou une instruction d’insertion dans la même opération) type d’instruction. Rails et ActiveRecord n’ont pas cette fonctionnalité. Vous pouvez toutefois utiliser le bijou inversé .

Sinon, vous pouvez utiliser: find_or_initialize_by ou find_or_create_by , qui offrent des fonctionnalités similaires, mais au prix d’un access à une firebase database supplémentaire, ce qui, dans la plupart des cas, ne pose find_or_create_by problème. Donc, à moins d’avoir de sérieux problèmes de performance, je n’utiliserais pas la gemme.

Par exemple, si aucun utilisateur n’est trouvé avec le nom “Roger”, une nouvelle instance d’utilisateur est instanciée avec son name défini sur “Roger”.

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

Vous pouvez également utiliser find_or_initialize_by .

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

En Rails 3.

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

Vous pouvez utiliser un bloc, mais le bloc s’exécute uniquement si l’enregistrement est nouveau .

 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 

Si vous souhaitez utiliser un bloc sans tenir compte de la persistance de l’enregistrement, tap sur le résultat:

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

Dans Rails 4, vous pouvez append un modèle spécifique:

 def self.update_or_create(atsortingbutes) assign_or_new(atsortingbutes).save end def self.assign_or_new(atsortingbutes) obj = first || new obj.assign_atsortingbutes(atsortingbutes) obj end 

et l’utiliser comme

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

Ou si vous préférez append ces méthodes à tous les modèles placés dans un initialiseur:

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

Ajoutez ceci à votre modèle:

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

Avec cela, vous pouvez:

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

Vous pouvez le faire dans une déclaration comme ceci:

 CachedObject.where(key: "the given key").first_or_create! do |cached| cached.atsortingbute1 = 'atsortingbute value' cached.atsortingbute2 = 'atsortingbute value' end 

Ancienne question mais jetant ma solution sur le ring pour être complet. J’avais besoin de cela quand j’avais besoin d’une découverte spécifique mais d’une création différente si elle n’existait pas.

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

La suite gem ajoute une méthode update_or_create qui semble faire ce que vous cherchez.