Dans Django, queryset fournit une méthode appelée get_or_create qui renvoie un objet ou crée un objet.
Cependant, comme la méthode get, get_or_create peut générer une exception si la requête renvoie plusieurs objets.
Existe-t-il une méthode pour le faire avec élégance:
objects = Model.manager.filter(params)
if len(objects) == 0:
obj = Model.objects.create(params)
else:
obj = objects[0]
get_or_create () n’est qu’une fonction pratique, il n’ya rien de mal à écrire vous-même, comme Pavid l’a montré ou
result = Model.objects.filter(field__lookup=value)[0]
if not result:
result = Model.objects.create(...)
return result
EDIT Comme suggéré, modifie la tranche [: 1] (qui renvoie une liste à entrée unique) après le filtre en [0] (qui renvoie l'objet réel). Le problème, c'est que cela déclenchera une exception s'il n'y a pas de correspondance avec la requête.
Cela déclenchera également une exception similaire:
Model.objects.filter(field_lookup=value).latest()
En repensant à la question, je ne suis pas sûr que l'affiche originale cherche à renvoyer plusieurs objets/lignes, ou simplement un moyen de contourner le fait de lever une exception lors de la récupération d'un seul objet/ligne.
Voici une autre option?
results = Model.objects.filter(...)
if results.exists():
return results
else:
return Model.objects.create(...)
et un autre:
result = None
try:
result = Model.objects.get(...)
except Model.DoesNotExist:
result = Model.objects.create(...)
Il n'y a rien de mal à élever et attraper des exceptions!
Depuis Django 1.6, il existe une méthode pratique first () qui renvoie le premier résultat dans une requête de filtrage, ou None.
obj = Model.manager.filter(params).first()
if obj is None:
obj = Model.objects.create(params)
Je viens de tomber sur cette question et je voulais y contribuer, car personne n’a suggéré de saisir IndexError.
try:
obj = Model.objects.filter(params)[0]
except IndexError:
obj = Model.objects.create(params)
Voici une méthode de gestion qui vous permettra d’étendre cette fonction avec élégance
class FilterOrCreateManager(models.Manager):
"""Adds filter_or_create method to objects
"""
def filter_or_create(self, **kwargs):
try:
created = False
obj = self.filter(**kwargs).first()
if obj is None:
obj = self.create(**kwargs)
created = True
return (obj,created)
except Exception as e:
print(e)
Ensuite, assurez-vous d’ajouter le gestionnaire aux modèles sur lesquels vous souhaitez l’utiliser:
class MyObj(models.Model):
objects = FilterOrCreateManager()
Ensuite, vous pourrez l’utiliser comme vous le feriez avec get_or_create
:
obj_instance, created = MyObj.filter_or_create(somearg='some value')
Vous pouvez essayer ceci:
result = Model.objects.filter(field__lookup=value)[0]
if not result:
result = Model.objects.create(...)
return result
Cela fonctionne pour moi:
À votre avis, appelez quelque chose comme ceci:
obj = Category.objects.get_or_create_category(form.cleaned_data.get('name'))
Dans votre gestionnaire de modèles, créez une fonction comme celle-ci:
class CategoryManager(models.Manager):
def get_or_create_category(self, query):
try:
result = Category.objects.filter(name = query)[0]
except:
result = Category.objects.create(name = query)
return result
La logique est simple. Tout d'abord, essayez de récupérer le premier objet Category dont le nom correspond à la chaîne de requête (fournie par le formulaire). Si la récupération échoue (car elle n'existe pas), créez une nouvelle catégorie avec la chaîne comme nom. Renvoie le résultat pour une utilisation dans votre vue.