102 votes

Django : Obtenez un objet de la base de données, ou 'None' si rien ne correspond.

Existe-t-il une fonction Django qui me permette de récupérer un objet dans la base de données, ou None si rien ne correspond ?

Pour l'instant, j'utilise quelque chose comme :

foo = Foo.objects.filter(bar=baz)
foo = len(foo) > 0 and foo.get() or None

Mais ce n'est pas très clair, et c'est compliqué d'en avoir partout.

2 votes

Vous savez que vous pouvez simplement utiliser foo = foo[0] if foo else None

2 votes

Python possède un opérateur ternaire, vous n'avez pas besoin d'utiliser les opérateurs booléens. Aussi, len(foo) est mauvais : " Note : N'utilisez pas len() sur les QuerySets si tout ce que vous voulez faire est de déterminer le nombre d'enregistrements dans le jeu. Il est beaucoup plus efficace de gérer un comptage au niveau de la base de données, en utilisant la méthode SELECT COUNT() de SQL, et Django fournit une méthode count() précisément pour cette raison. ". Réécrit : foo = foo[0] if foo.exists() else None

144voto

Nick Craig-Wood Points 18742

Il y a deux façons de procéder ;

try:
    foo = Foo.objects.get(bar=baz)
except model.DoesNotExist:
    foo = None

Ou vous pouvez utiliser un wrapper :

def get_or_none(model, *args, **kwargs):
    try:
        return model.objects.get(*args, **kwargs)
    except model.DoesNotExist:
        return None

Appelez ça comme ça

foo = get_or_none(Foo, baz=bar)

8 votes

Je n'aime pas le fait qu'il utilise des exceptions pour la fonctionnalité - c'est une mauvaise pratique dans la plupart des langages. Comment cela se passe-t-il en python ?

18 votes

C'est ainsi que l'itération en Python fonctionne (soulève un StopIteration), et c'est une partie essentielle du langage. Je ne suis pas non plus un grand fan de la fonctionnalité try/except, mais elle semble être considérée comme Python.

5 votes

@pcv : il n'existe pas de sagesse conventionnelle fiable concernant "la plupart" des langues. Je suppose que vous pensez à un langage spécifique que vous utilisez, mais soyez prudent avec de tels quantificateurs. Les exceptions peuvent être aussi lentes (ou "assez rapides" d'ailleurs) que n'importe quoi en Python. Pour en revenir à la question, c'est une lacune du framework, qu'ils n'ont pas fourni d'outils de gestion des exceptions. queryset pour le faire avant la version 1.6 ! Mais en ce qui concerne cette construction, elle est simplement maladroite. Elle n'est pas "mauvaise" parce qu'un auteur de tutoriel de votre langage préféré l'a dit. Essayez de chercher sur Google : "demander le pardon plutôt que la permission".

90voto

Cesar Canassa Points 3579

Dans Django 1.6 vous pouvez utiliser le first() Méthode Queryset. Elle renvoie le premier objet correspondant au queryset, ou None s'il n'y a pas d'objet correspondant.

Utilisation :

p = Article.objects.order_by('title', 'pub_date').first()

20 votes

Ce n'est pas une bonne réponse, si plusieurs objets sont trouvés alors MultipleObjectsReturned() n'est pas soulevée, comme l'indique le document aquí . Vous pouvez trouver de meilleures réponses aquí

26 votes

@sleepycal Je ne suis pas d'accord. Le site first() fonctionne exactement comme il est supposé le faire. Il renvoie le premier objet du résultat de la requête et ne se soucie pas de savoir si plusieurs résultats sont trouvés. Si vous avez besoin de vérifier si plusieurs objets sont retournés, vous devez utiliser la fonction .get() à la place.

7 votes

[Comme le dit l'OP, .get() n'est pas adapté à ses besoins. La réponse correcte à cette question peut être trouvée ci-dessous par @kaapstorm, et est clairement la réponse la plus appropriée. Abus de filter() cette façon peut conduire à des conséquences inattendues, ce que le PO n'a probablement pas réalisé (à moins que je ne manque quelque chose ici).

84voto

kaapstorm Points 596

Pour ajouter un exemple de code à la réponse de sorki (je l'ajouterais bien en commentaire, mais c'est mon premier message, et je n'ai pas assez de réputation pour laisser des commentaires), j'ai implémenté un gestionnaire personnalisé get_or_none comme suit :

from django.db import models

class GetOrNoneManager(models.Manager):
    """Adds get_or_none method to objects
    """
    def get_or_none(self, **kwargs):
        try:
            return self.get(**kwargs)
        except self.model.DoesNotExist:
            return None

class Person(models.Model):
    name = models.CharField(max_length=255)
    objects = GetOrNoneManager()

Et maintenant je peux le faire :

bob_or_none = Person.objects.get_or_none(name='Bob')

0 votes

C'est très élégant.

14voto

llazzaro Points 1392

Vous pouvez aussi essayer d'utiliser django annoying (il a d'autres fonctions utiles !)

l'installer avec :

pip install django-annoying

from annoying.functions import get_object_or_None
get_object_or_None(Foo, bar=baz)

10voto

sorki Points 411

Donner Foo son gestionnaire personnalisé . C'est assez facile - il suffit de mettre votre code dans la fonction du gestionnaire personnalisé, de définir le gestionnaire personnalisé dans votre modèle et de l'appeler avec Foo.objects.your_new_func(...) .

Si vous avez besoin d'une fonction générique (pour l'utiliser sur n'importe quel modèle et pas seulement sur celui qui a un gestionnaire personnalisé), écrivez la vôtre et placez-la quelque part dans votre chemin python et importez-la, ce n'est plus la pagaille.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X