Domanda

Stavo discutendo di questo con alcuni colleghi. C'è un modo preferito per recuperare un oggetto in Django quando te ne aspetti solo uno?

I due modi ovvi sono:

try:
    obj = MyModel.objects.get(id=1)
except MyModel.DoesNotExist:
    # We have no object! Do something...
    pass

E

objs = MyModel.objects.filter(id=1)

if len(objs) == 1:
    obj = objs[0]
else:
    # We have no object! Do something...
    pass

Il primo metodo sembra comportamentalmente più corretto, ma utilizza eccezioni nel flusso di controllo che possono introdurre un sovraccarico. La seconda è più rotonda ma non solleverà mai un'eccezione.

Qualche idea su quale di questi sia preferibile? Qual è più efficiente?

È stato utile?

Soluzione

get () viene fornito appositamente per questo caso. Usalo.

L'opzione 2 è quasi esattamente il modo in cui il metodo get () è effettivamente implementato in Django, quindi non ci dovrebbero essere "prestazioni". differenza (e il fatto che ci stai pensando indica che stai violando una delle regole cardine della programmazione, vale a dire cercare di ottimizzare il codice prima ancora che sia stato scritto e profilato - finché non hai il codice e puoi eseguirlo, tu non so come funzionerà e cercare di ottimizzare prima di allora è un percorso di dolore).

Altri suggerimenti

Puoi installare un modulo chiamato django-fastidioso e quindi fare questo:

from annoying.functions import get_object_or_None

obj = get_object_or_None(MyModel, id=1)

if not obj:
    #omg the object was not found do some error stuff

1 è corretto. In Python un'eccezione ha lo stesso sovraccarico di un ritorno. Per una dimostrazione semplificata puoi guardare questo .

2 Questo è ciò che Django sta facendo nel backend. get chiama filter e genera un'eccezione se non viene trovato alcun elemento o se viene trovato più di un oggetto.

Sono un po 'in ritardo alla festa, ma con Django 1.6 c'è il metodo first () sui queryset.

https: / /docs.djangoproject.com/en/dev/ref/models/querysets/#django.db.models.query.QuerySet.first


  

Restituisce il primo oggetto corrispondente al set di query o Nessuno se non è presente alcun oggetto corrispondente. Se QuerySet non ha alcun ordinamento definito, il queryset viene automaticamente ordinato dalla chiave primaria.

Esempio:

p = Article.objects.order_by('title', 'pub_date').first()
Note that first() is a convenience method, the following code sample is equivalent to the above example:

try:
    p = Article.objects.order_by('title', 'pub_date')[0]
except IndexError:
    p = None

Non posso parlare con nessuna esperienza di Django ma l'opzione n. 1 dice chiaramente al sistema che stai chiedendo 1 oggetto, mentre la seconda opzione no. Ciò significa che l'opzione n. 1 potrebbe sfruttare più facilmente gli indici della cache o del database, in particolare quando l'attributo su cui si sta filtrando non è garantito come unico.

Inoltre (di nuovo, ipotizzando) la seconda opzione potrebbe dover creare una sorta di raccolta di risultati o oggetto iteratore poiché la chiamata filter () potrebbe normalmente restituire molte righe. Lo aggireresti con get ().

Infine, la prima opzione è sia più breve che omette la variabile temporanea aggiuntiva - solo una differenza minore ma ogni piccolo aiuto.

Perché tutto ciò funziona? Sostituisci 4 righe con 1 scorciatoia integrata. (Questo fa il suo tentativo / tranne.)

from django.shortcuts import get_object_or_404

obj = get_object_or_404(MyModel, id=1)

Altre informazioni sulle eccezioni. Se non vengono allevati, non costano quasi nulla. Quindi, se sai che probabilmente otterrai un risultato, usa l'eccezione, poiché usando un'espressione condizionale pagherai il costo del controllo ogni volta, qualunque cosa accada. D'altra parte, costano un po 'più di un'espressione condizionale quando vengono sollevati, quindi se ti aspetti di non avere un risultato con una certa frequenza (diciamo, il 30% delle volte, se la memoria serve), il controllo condizionale risulta essere un po 'più economico.

Ma questo è l'ORM di Django, e probabilmente il viaggio di andata e ritorno nel database, o anche un risultato memorizzato nella cache, probabilmente dominerà le caratteristiche delle prestazioni, quindi favorisci la leggibilità, in questo caso, poiché ti aspetti esattamente un risultato, usa < code> get () .

Ho giocato un po 'con questo problema e ho scoperto che l'opzione 2 esegue due query SQL, che per un'attività così semplice è eccessiva. Vedi la mia annotazione:

objs = MyModel.objects.filter(id=1) # This does not execute any SQL
if len(objs) == 1: # This executes SELECT COUNT(*) FROM XXX WHERE filter
    obj = objs[0]  # This executes SELECT x, y, z, .. FROM XXX WHERE filter
else: 
    # we have no object!  do something
    pass

Una versione equivalente che esegue una singola query è:

items = [item for item in MyModel.objects.filter(id=1)] # executes SELECT x, y, z FROM XXX WHERE filter
count = len(items) # Does not execute any query, items is a standard list.
if count == 0:
   return None
return items[0]

Passando a questo approccio, sono stato in grado di ridurre sostanzialmente il numero di query eseguite dalla mia applicazione.

Domanda interessante, ma per me l'opzione n. 2 puzza di ottimizzazione prematura. Non sono sicuro di quale sia il più performante, ma l'opzione n. 1 sembra sicuramente più ritmata per me.

Suggerisco un design diverso.

Se si desidera eseguire una funzione su un possibile risultato, è possibile derivare da QuerySet, in questo modo: http: //djangosnippets.org/snippets/734/

Il risultato è piuttosto fantastico, potresti ad esempio:

MyModel.objects.filter(id=1).yourFunction()

Qui, il filtro restituisce un queryset vuoto o un queryset con un singolo elemento. Le tue funzioni di query personalizzate sono anche concatenabili e riutilizzabili. Se si desidera eseguirlo per tutte le voci: MyModel.objects.all (). YourFunction () .

Sono inoltre ideali per essere utilizzati come azioni nell'interfaccia di amministrazione:

def yourAction(self, request, queryset):
    queryset.yourFunction()

L'opzione 1 è più elegante, ma assicurati di usare try..except.

Dalla mia esperienza posso dirti che a volte sei sicuro che non ci possa essere più di un oggetto corrispondente nel database, eppure ci saranno due ... (tranne ovviamente quando si ottiene l'oggetto dal suo primario chiave).

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top