Wie erhalte ich das Objekt, wenn es existiert, oder None, wenn es nicht existiert?

222

Wenn ich den Modellmanager auffordere, ein Objekt abzurufen, wird es ausgelöst, DoesNotExistwenn kein übereinstimmendes Objekt vorhanden ist.

go = Content.objects.get(name="baby")

Statt DoesNotExist, wie kann ich gosein , Nonestatt?

TIMEX
quelle

Antworten:

332

Es gibt keine "eingebaute" Möglichkeit, dies zu tun. Django löst jedes Mal die DoesNotExist-Ausnahme aus. Die idiomatische Möglichkeit, dies in Python zu handhaben, besteht darin, es in einen Try-Catch zu packen:

try:
    go = SomeModel.objects.get(foo='bar')
except SomeModel.DoesNotExist:
    go = None

Was ich getan habe, ist, Modelle in Unterklassen zu unterteilen. Manager, erstellen Sie einen safe_getähnlichen Code wie oben und verwenden Sie diesen Manager für meine Modelle. Auf diese Weise können Sie schreiben : SomeModel.objects.safe_get(foo='bar').

Arthur Debert
quelle
7
Gute Verwendung von SomeModel.DoesNotExist, anstatt auch die Ausnahme zu importieren.
Supermitch
195
Diese Lösung ist vier Zeilen lang. Für mich ist das zu viel. Mit django 1.6 können Sie SomeModel.objects.filter(foo='bar').first()das erste Match oder None zurückgeben. Es scheitert nicht, wenn es mehrere Fälle wiequeryset.get()
guettli
9
Ich denke, es ist ein schlechter Stil, Ausnahmen für die Behandlung von Standardfällen zu überbeanspruchen. Ja, "es ist einfacher, um Vergebung zu bitten als um Erlaubnis". Aber eine Ausnahme sollte in meinen Augen immer noch für Ausnahmen verwendet werden.
Konstantin Schubert
8
Explizit ist besser als implizit. Es sei denn, es gibt einen Leistungsgrund für die Verwendung. filter().first()Ich denke, die Ausnahme ist der richtige Weg.
Christianbundy
6
Die Verwendung von first () ist nur dann eine gute Idee, wenn es Ihnen egal ist, ob es mehrere gibt. Andernfalls ist diese Lösung überlegen, da sie immer noch eine Ausnahme auslöst, wenn Sie unerwartet mehrere Objekte finden. Dies ist normalerweise der Fall, den Sie in diesem Fall wünschen.
Rossdavidh
181

Seit django 1.6 können Sie die first () -Methode wie folgt verwenden :

Content.objects.filter(name="baby").first()
FeroxTL
quelle
26
In diesem Fall wird kein Fehler ausgelöst, wenn mehr als eine Übereinstimmung vorliegt.
Konstantin Schubert
7
'FeroxTL' Sie müssen @guettli für diese Antwort gutschreiben, da er dies ein Jahr vor Ihrem Beitrag zu der akzeptierten Antwort kommentiert hat.
colm.anseo
7
@colminator Ich würde eher sagen, dass guettli lernen sollte, dass eine neue Antwort nicht als Kommentar gehört, wenn er seinen Ruf als Stackoverflow erhöhen möchte :) FeroxTL sollte Punkte erhalten, um etwas als Kommentar als Antwort klarer zu machen. Ihr Kommentar ist genug für Guettli, denke ich, und sollte nicht zur Antwort hinzugefügt werden, wenn dies Ihr Vorschlag war.
Joakim
3
@ Joakim Ich habe kein Problem mit dem Posten einer neuen "Antwort" - nur um Kredit zu geben, wo es fällig ist :-)
colm.anseo
3
Was ist mit der Leistung dieses Ansatzes im Vergleich zur akzeptierten Antwort?
MaxCore
36

Aus Django-Dokumenten

get()löst eine DoesNotExistAusnahme aus, wenn für die angegebenen Parameter kein Objekt gefunden wird. Diese Ausnahme ist auch ein Attribut der Modellklasse. Die DoesNotExist Ausnahme erbt vondjango.core.exceptions.ObjectDoesNotExist

Sie können die Ausnahme abfangen und zuweisen None, um zu gehen.

from django.core.exceptions import ObjectDoesNotExist
try:
    go  = Content.objects.get(name="baby")
except ObjectDoesNotExist:
    go = None
Amarghosh
quelle
33

Hierfür können Sie eine generische Funktion anlegen.

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

Verwenden Sie dies wie folgt:

go = get_or_none(Content,name="baby")

go ist None, wenn kein anderer Eintrag übereinstimmt, wird der Content-Eintrag zurückgegeben.

Hinweis: Es wird eine Ausnahme ausgelöst. MultipleObjectsReturned, wenn mehr als ein Eintrag für name = "baby" zurückgegeben wird.

Ranju R.
quelle
14

Um die Sache zu vereinfachen, hier ein Ausschnitt des Codes, den ich geschrieben habe, basierend auf den Eingaben aus den wunderbaren Antworten hier:

class MyManager(models.Manager):

    def get_or_none(self, **kwargs):
        try:
            return self.get(**kwargs)
        except ObjectDoesNotExist:
            return None

Und dann in Ihrem Modell:

class MyModel(models.Model):
    objects = MyManager()

Das ist es. Jetzt haben Sie MyModel.objects.get () sowie MyModel.objetcs.get_or_none ()

Moti Radomski
quelle
7
Vergessen Sie auch nicht zu importieren: von django.core.exceptions importieren ObjectDoesNotExist
Moti Radomski
13

Sie könnten existsmit einem Filter verwenden:

Content.objects.filter(name="baby").exists()
#returns False or True depending on if there is anything in the QS

Nur eine Alternative, wenn Sie nur wissen möchten, ob es existiert

Ryan Saxe
quelle
4
Dies würde einen zusätzlichen Datenbankaufruf verursachen, wenn vorhanden. Keine gute Idee
Christoffer
@Christoffer nicht sicher, warum das ein zusätzlicher Datenbankaufruf sein sollte. Gemäß den Dokumenten :Note: If you only want to determine if at least one result exists (and don’t need the actual objects), it’s more efficient to use exists().
Anupam
2
@Christoffer Ich denke du hast recht. Ich habe die Frage jetzt noch einmal gelesen und das OP möchte tatsächlich, dass das eigentliche Objekt zurückgegeben wird. So exists()wird mit verwendet werden ifKlausel bevor das Objekt daher holen einen doppelten Schlag auf den db verursacht. Ich werde den Kommentar trotzdem behalten, falls er jemand anderem hilft.
Anupam
7

Das Behandeln von Ausnahmen an verschiedenen Stellen in Ihren Ansichten kann sehr umständlich sein. Wie wäre es mit dem Definieren eines benutzerdefinierten Modellmanagers in der Datei models.py, z

class ContentManager(model.Manager):
    def get_nicely(self, **kwargs):
        try:
            return self.get(kwargs)
        except(KeyError, Content.DoesNotExist):
            return None

und dann in die Content Model-Klasse aufnehmen

class Content(model.Model):
    ...
    objects = ContentManager()

Auf diese Weise kann es leicht in den Ansichten behandelt werden, dh

post = Content.objects.get_nicely(pk = 1)
if post:
    # Do something
else:
    # This post doesn't exist
Adil Malik
quelle
1
Ich mag diese Lösung wirklich, konnte sie aber nicht so zum Laufen bringen, wie es bei Verwendung von Python 3.6 der Fall ist. Wollte eine Notiz hinterlassen, dass die Rückgabe im ContentManager geändert wurde, damit return self.get(**kwargs)sie für mich funktioniert. Um nicht zu sagen, dass irgendetwas mit der Antwort nicht stimmt, nur ein Tipp für alle, die versuchen, sie mit späteren Versionen zu verwenden (oder mit was auch immer, was sie davon abgehalten hat, für mich zu funktionieren).
Skagzilla
7

Dies ist eine dieser nervigen Funktionen , die Sie möglicherweise nicht erneut implementieren möchten:

from annoying.functions import get_object_or_None
#...
user = get_object_or_None(Content, name="baby")
mknecht
quelle
Ich habe den Code von überprüft, get_object_or_Noneaber festgestellt, dass er immer noch ausgelöst wird, MultipleObjectsReturnedwenn mehr als ein Objekt vorhanden ist. Daher könnte der Benutzer in Betracht ziehen, mit a zu umgeben try-except(was die Funktion selbst try-exceptbereits hat).
John Pang
4

Wenn Sie eine einfache einzeilige Lösung wünschen, die keine Ausnahmebehandlung, bedingte Anweisungen oder eine Anforderung von Django 1.6+ umfasst, gehen Sie stattdessen folgendermaßen vor:

x = next(iter(SomeModel.objects.filter(foo='bar')), None)
blhsing
quelle
4

Ich denke, es ist keine schlechte Idee zu verwenden get_object_or_404()

from django.shortcuts import get_object_or_404

def my_view(request):
    my_object = get_object_or_404(MyModel, pk=1)

Dieses Beispiel entspricht:

from django.http import Http404

def my_view(request):
    try:
        my_object = MyModel.objects.get(pk=1)
    except MyModel.DoesNotExist:
        raise Http404("No MyModel matches the given query.")

Weitere Informationen zu get_object_or_404 () finden Sie in der Online-Dokumentation zu django.

Mohammad Reza
quelle
2

Ab Django 1.7 können Sie Folgendes tun:

class MyQuerySet(models.QuerySet):

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


class MyBaseModel(models.Model):

    objects = MyQuerySet.as_manager()


class MyModel(MyBaseModel):
    ...

class AnotherMyModel(MyBaseModel):
    ...

Der Vorteil von "MyQuerySet.as_manager ()" besteht darin, dass beide der folgenden Funktionen funktionieren:

MyModel.objects.filter(...).get_or_none()
MyModel.objects.get_or_none()
Vinayak Kaniyarakkal
quelle
1

Hier ist eine Variation der Hilfsfunktion, mit der Sie optional eine QuerySetInstanz übergeben können, falls Sie das eindeutige Objekt (falls vorhanden) aus einem anderen Abfragesatz als dem Objektabfragesatz des Modells allabrufen möchten (z. B. aus einer Teilmenge von untergeordneten Elementen, die zu a gehören übergeordnete Instanz):

def get_unique_or_none(model, queryset=None, **kwargs):
    """
        Performs the query on the specified `queryset`
        (defaulting to the `all` queryset of the `model`'s default manager)
        and returns the unique object matching the given
        keyword arguments.  Returns `None` if no match is found.
        Throws a `model.MultipleObjectsReturned` exception
        if more than one match is found.
    """
    if queryset is None:
        queryset = model.objects.all()
    try:
        return queryset.get(**kwargs)
    except model.DoesNotExist:
        return None

Dies kann auf zwei Arten verwendet werden, z.

  1. obj = get_unique_or_none(Model, **kwargs) wie zuvor besprochen
  2. obj = get_unique_or_none(Model, parent.children, **kwargs)
Gary
quelle
1

Ohne Ausnahme:

if SomeModel.objects.filter(foo='bar').exists():
    x = SomeModel.objects.get(foo='bar')
else:
    x = None

Verwenden einer Ausnahme:

try:
   x = SomeModel.objects.get(foo='bar')
except SomeModel.DoesNotExist:
   x = None

Es gibt ein wenig Streit darüber, wann man in Python eine Ausnahme verwenden sollte. Einerseits "ist es einfacher, um Vergebung zu bitten als um Erlaubnis". Obwohl ich damit einverstanden bin, glaube ich, dass eine Ausnahme bleiben sollte, nun, die Ausnahme, und der "Idealfall" sollte laufen, ohne eine zu treffen.

Konstantin Schubert
quelle
1

Wir können die in Django integrierte Ausnahme verwenden, die an die als .DoesNotExist. Wir müssen also keine ObjectDoesNotExistAusnahme importieren .

Stattdessen tun:

from django.core.exceptions import ObjectDoesNotExist

try:
    content = Content.objects.get(name="baby")
except ObjectDoesNotExist:
    content = None

Wir können das schaffen:

try:
    content = Content.objects.get(name="baby")
except Content.DoesNotExist:
    content = None
Zakiakhmad
quelle
0

Dies ist eine Nachahmerin von Djangos get_object_or_404, außer dass die Methode None zurückgibt. Dies ist äußerst nützlich, wenn wir die only()Abfrage verwenden müssen, um nur bestimmte Felder abzurufen. Diese Methode kann ein Modell oder einen Abfragesatz akzeptieren.

from django.shortcuts import _get_queryset


def get_object_or_none(klass, *args, **kwargs):
    """
    Use get() to return an object, or return None if object
    does not exist.
    klass may be a Model, Manager, or QuerySet object. All other passed
    arguments and keyword arguments are used in the get() query.
    Like with QuerySet.get(), MultipleObjectsReturned is raised if more than
    one object is found.
    """
    queryset = _get_queryset(klass)
    if not hasattr(queryset, 'get'):
        klass__name = klass.__name__ if isinstance(klass, type) else klass.__class__.__name__
        raise ValueError(
            "First argument to get_object_or_none() must be a Model, Manager, "
            "or QuerySet, not '%s'." % klass__name
        )
    try:
        return queryset.get(*args, **kwargs)
    except queryset.model.DoesNotExist:
        return None
Sandeep Balagopal
quelle
0

Vielleicht ist es besser, wenn Sie verwenden:

User.objects.filter(username=admin_username).exists()
Jonathan Souza
quelle