Quando chiedo al gestore modelli di ottenere un oggetto, si solleva DoesNotExist
quando non ci sono oggetti corrispondenti.
go = Content.objects.get(name="baby")
Invece di DoesNotExist
, come posso avere go
essere None
invece?
Risposte:
Non esiste un modo "integrato" per farlo. Django solleverà l'eccezione DoesNotExist ogni volta. Il modo idiomatico per gestire questo in Python è avvolgerlo in un tentativo di cattura:
try:
go = SomeModel.objects.get(foo='bar')
except SomeModel.DoesNotExist:
go = None
Quello che ho fatto è creare una sottoclasse di modelli Gestore, creare un safe_get
codice simile al precedente e utilizzare quel gestore per i miei modelli. In questo modo è possibile scrivere: SomeModel.objects.safe_get(foo='bar')
.
SomeModel.objects.filter(foo='bar').first()
questo restituisce la prima corrispondenza o Nessuno. Non fallisce se ci sono diversi casi comequeryset.get()
filter().first()
penso che l'eccezione sia la strada da percorrere.
Da django 1.6 puoi usare il metodo first () in questo modo:
Content.objects.filter(name="baby").first()
get()
sollevaDoesNotExist
un'eccezione se un oggetto non viene trovato per i parametri dati. Questa eccezione è anche un attributo della classe del modello. L'DoesNotExist
eccezione eredita dadjango.core.exceptions.ObjectDoesNotExist
Puoi catturare l'eccezione e assegnare None
per andare.
from django.core.exceptions import ObjectDoesNotExist
try:
go = Content.objects.get(name="baby")
except ObjectDoesNotExist:
go = None
È possibile creare una funzione generica per questo.
def get_or_none(classmodel, **kwargs):
try:
return classmodel.objects.get(**kwargs)
except classmodel.DoesNotExist:
return None
Usa questo come di seguito:
go = get_or_none(Content,name="baby")
go
sarà None
se nessuna voce corrisponde altrimenti restituirà la voce Contenuto.
Nota: solleverà un'eccezione MultipleObjectsReturned
se viene restituita più di una voce per name="baby"
.
Dovresti gestirlo sul modello dati per evitare questo tipo di errore, ma potresti preferirlo registrarlo in fase di esecuzione in questo modo:
def get_or_none(classmodel, **kwargs):
try:
return classmodel.objects.get(**kwargs)
except classmodel.MultipleObjectsReturned as e:
print('ERR====>', e)
except classmodel.DoesNotExist:
return None
Puoi farlo in questo modo:
go = Content.objects.filter(name="baby").first()
Ora vai variabile potrebbe essere l'oggetto che desideri o Nessuno
Rif: https://docs.djangoproject.com/en/1.8/ref/models/querysets/#django.db.models.query.QuerySet.first
Per rendere le cose più facili, ecco uno snippet del codice che ho scritto, basato sugli input dalle meravigliose risposte qui:
class MyManager(models.Manager):
def get_or_none(self, **kwargs):
try:
return self.get(**kwargs)
except ObjectDoesNotExist:
return None
E poi nel tuo modello:
class MyModel(models.Model):
objects = MyManager()
Questo è tutto. Ora hai MyModel.objects.get () e MyModel.objetcs.get_or_none ()
potresti usare exists
con un filtro:
Content.objects.filter(name="baby").exists()
#returns False or True depending on if there is anything in the QS
solo un'alternativa per se vuoi solo sapere se esiste
exists()
verrà usato con la if
clausola prima di recuperare l'oggetto, causando quindi un doppio colpo al db. Terrò comunque il commento in giro nel caso in cui aiuti qualcun altro.
Gestire le eccezioni in diversi punti delle tue viste potrebbe essere davvero complicato ... Che ne dici di definire un gestore modelli personalizzato, nel file models.py, come
class ContentManager(model.Manager):
def get_nicely(self, **kwargs):
try:
return self.get(kwargs)
except(KeyError, Content.DoesNotExist):
return None
e quindi includerlo nella classe del modello di contenuto
class Content(model.Model):
...
objects = ContentManager()
In questo modo può essere facilmente affrontato nelle viste es
post = Content.objects.get_nicely(pk = 1)
if post:
# Do something
else:
# This post doesn't exist
return self.get(**kwargs)
farlo funzionare per me. Per non dire che c'è qualcosa di sbagliato nella risposta, solo un suggerimento per chiunque cerchi di usarlo con le versioni successive (o con qualsiasi altra cosa gli abbia impedito di funzionare per me).
È una di quelle funzioni fastidiose che potresti non voler reimplementare:
from annoying.functions import get_object_or_None
#...
user = get_object_or_None(Content, name="baby")
get_object_or_None
ma ho scoperto che si solleva ancora MultipleObjectsReturned
se più di un oggetto. Quindi, l'utente potrebbe considerare di circondare con a try-except
(che la funzione stessa ha try-except
già).
Penso che non sia una cattiva idea da usare 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)
Questo esempio è equivalente a:
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.")
Puoi leggere di più su get_object_or_404 () nella documentazione in linea di django.
Da django 1.7 in poi puoi fare come:
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):
...
Il vantaggio di "MyQuerySet.as_manager ()" è che entrambi i seguenti elementi funzioneranno:
MyModel.objects.filter(...).get_or_none()
MyModel.objects.get_or_none()
Ecco una variazione sulla funzione di supporto che ti consente di passare facoltativamente QuerySet
un'istanza, nel caso in cui desideri ottenere l'oggetto univoco (se presente) da un set di query diverso dal set di all
query degli oggetti del modello (ad esempio da un sottoinsieme di elementi figlio appartenenti a un istanza genitore):
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
Questo può essere utilizzato in due modi, ad esempio:
obj = get_unique_or_none(Model, **kwargs)
come discusso in precedenzaobj = get_unique_or_none(Model, parent.children, **kwargs)
Senza eccezioni:
if SomeModel.objects.filter(foo='bar').exists():
x = SomeModel.objects.get(foo='bar')
else:
x = None
Utilizzando un'eccezione:
try:
x = SomeModel.objects.get(foo='bar')
except SomeModel.DoesNotExist:
x = None
C'è un po 'di discussione su quando si dovrebbe usare un'eccezione in Python. Da un lato, "è più facile chiedere perdono che permesso". Anche se sono d'accordo con questo, credo che dovrebbe rimanere un'eccezione, beh, l'eccezione e il "caso ideale" dovrebbe funzionare senza colpirne uno.
Possiamo usare l'eccezione incorporata Django che è collegata ai modelli denominati come .DoesNotExist
. Quindi, non dobbiamo importare ObjectDoesNotExist
eccezioni.
Invece facendo:
from django.core.exceptions import ObjectDoesNotExist
try:
content = Content.objects.get(name="baby")
except ObjectDoesNotExist:
content = None
Possiamo farcela:
try:
content = Content.objects.get(name="baby")
except Content.DoesNotExist:
content = None
Forse è meglio che usi:
User.objects.filter(username=admin_username).exists()
Questo è un copione da get_object_or_404 di Django tranne per il fatto che il metodo restituisce None. Ciò è estremamente utile quando dobbiamo utilizzare la only()
query per recuperare solo determinati campi. Questo metodo può accettare un modello o un set di query.
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
Anch'io stavo affrontando lo stesso problema. È difficile scrivere e leggere try-except
ogni volta che vuoi ottenere un elemento dal tuo modello come nella risposta di @Arthur Debert. Quindi, la mia soluzione è creare una Getter
classe ereditata dai modelli:
class Getter:
@classmethod
def try_to_get(cls, *args, **kwargs):
try:
return cls.objects.get(**kwargs)
except Exception as e:
return None
class MyActualModel(models.Model, Getter):
pk_id = models.AutoField(primary_key=True)
...
In questo modo, posso ottenere l'elemento effettivo di MyActualModel
o None
:
MyActualModel.try_to_get(pk_id=1)
Uso Django 2.2.16. Ed è così che risolvo questo problema:
from typing import Any
from django.core.exceptions import ObjectDoesNotExist
from django.db import models
from django.db.models.base import ModelBase
from django.db.models.manager import Manager
class SManager(Manager):
def get_if_exist(self, *args: Any, **kwargs: Any):
try:
return self.get(*args, **kwargs)
except ObjectDoesNotExist:
return None
class SModelBase(ModelBase):
def _prepare(cls):
manager = SManager()
manager.auto_created = True
cls.add_to_class("objects", manager)
super()._prepare()
class Meta:
abstract = True
class SModel(models.Model, metaclass=SModelBase):
managers = False
class Meta:
abstract = True
Dopodiché, in ogni modello, devi solo importare in:
from custom.models import SModel
class SUser(SModel):
pass
E in views
, puoi chiamare in questo modo:
SUser.objects.get_if_exist(id=1)