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.