web-dev-qa-db-fra.com

Django: récupère le premier objet d'une requête de filtrage ou de création

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]
21
arustgi

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!

30
Timmy O'Mahony

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)
20
Sean

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)
5
demux

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')
4
Duncan

Vous pouvez essayer ceci:

result = Model.objects.filter(field__lookup=value)[0]
if not result:
   result = Model.objects.create(...)
return result
1
Afsa Ahmed

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. 

0
bento