2013-06-30 8 views
7

Ho il seguente schema di modello in Django (con Postgres).Django: recupero di ID di molti campi molto velocemente

class A(Models.model): 
    related = models.ManyToManyField("self", null=True) 

dato un QuerySet di A, vorrei tornare una mappatura dizionario ogni istanza di A nel QuerySet a un elenco di id s dei suoi related casi il più rapidamente possibile.

Posso sicuramente scorrere ogni A e interrogare il campo correlato, ma esiste un modo più ottimale?

+0

Hai mai trovato una soluzione a questo? – cazgp

risposta

8

In base a tre istanze. È possibile utilizzare il metodo values_list per recuperare solo i risultati e da questo risultato ottenere solo gli ID delle loro istanze related. Io uso il campo pk per essere il mio filtro perché non conosco il tuo schema, ma puoi usare qualsiasi cosa, solo deve essere un QuerySet.

>>> result = A.objects.filter(pk=1) 
>>> result.values('related__id') 
[{'id': 2}, {'id': 3}] 
>>> result.values_list('related__id') 
[(2,), (3,)] 
>>> result.values_list('related__id', flat=True) 
[2, 3] 
+1

Questo è quello che ho fatto, ma il problema è che ho bisogno di filtrare su più di un 'pk' (ad esempio' A.objects.filter (pk__in = [1,2,6]) '). Mi piacerebbe un elenco di liste, in cui l'ennesima lista interna è simile a 'values_list' qui, ma corrisponde all'ennesimo' pk'. –

+0

È anche possibile eseguire instance.related.values ​​('id', flat = True), se si dispone già di una singola istanza di modello. – alexcasalboni

0

È possibile ottenere abbastanza vicino in questo modo:

qs = A.objects.prefetch_related(Prefetch(
         'related', 
         queryset=A.objects.only('pk'), 
         to_attr='related_insts')).in_bulk(my_list_of_pks) 

questo darà una mappatura da PKS dell'oggetto corrente all'istanza stessa, in modo da poter scorrere come segue:

for pk, inst in qs.iteritems(): 
    related_ids = (related.pk for related in inst.related_insts) 

o dato un esempio, si può fare una ricerca veloce in questo modo:

related_ids = (related.pk for related in qs[instance.pk]). 

Questo metodo associa gli id ​​di istanza agli ID correlati (indirettamente) poiché si richiede specificamente un dizionario. Se non si sta facendo le ricerche, si consiglia il seguente invece:

qs = A.objects.prefetch_related(Prefetch(
     'related', 
     queryset=A.objects.only('pk'), 
     to_attr='related_insts')).filter(pk__in=my_list_of_pks) 
for inst in qs: 
    related_ids = (related.pk for related in inst.related_insts) 

si può prendere atto della uso di only a tirare solo le PKS dal db. C'è un open ticket per consentire l'uso di values e (presumo) values_list nelle query di Prefetch. Questo ti permetterebbe di fare quanto segue.

qs = A.objects.prefetch_related(Prefetch(
     'related', 
     queryset=A.objects.values_list('pk', flat=True), 
     to_attr='related_ids')).filter(pk__in=my_list_of_pks) 
for inst in qs: 
    related_ids = inst.related_ids 

Si potrebbe naturalmente ottimizzare ulteriormente, ad esempio utilizzando qs.only('related_insts') sul set di query primario, ma assicurarsi che non si sta facendo nulla con questi instances-- sono essenzialmente solo contenitori costosi per tenere il vostro related_ids.

Credo che questo sia il migliore disponibile per ora (senza query personalizzate). Per raggiungere esattamente quello che vuoi, sono necessarie due cose:

  1. La funzione di cui sopra è implementata
  2. values_list è fatto per lavorare con Prefetch to_attr, come invece accade per le annotazioni.

Con queste due cose a posto (e continuando l'esempio precedente) si potrebbe fare quanto segue per ottenere esattamente quello che avete richiesto:

d = qs.values_list('related_ids', flat=True).in_bulk() 
for pk, related_pks in d: 
    print 'Containing Objects %s' % pk 
    print 'Related objects %s' % related_pks 
# And lookups 
print 'Object %d has related objects %s' % (20, d[20]) 

ho lasciato fuori alcuni dettagli spiegare le cose, ma è dovrebbe essere abbastanza chiaro dalla documentazione. Se hai bisogno di chiarimenti, non esitare!

Problemi correlati