model manytomany - Django Queryset con filtro sulla chiave esterna inversa




one example (5)

Ho il seguente modello Django:

class Make:
   name = models.CharField(max_length=200)

class MakeContent:
   make = models.ForeignKey(Make)
   published = models.BooleanField()

Mi piacerebbe sapere se è possibile (senza scrivere direttamente SQL) per generare un queryset che contenga tutti i Make e i relativi MakeContent relativi a dove è published = True .


Answers

Django non supporta il metodo select_related () per le ricerche inverse di chiavi esterne, quindi il meglio che puoi fare senza lasciare Python sono due query di database. Il primo è quello di prendere tutti i Make che contengono MakeContents dove pubblicati = True, e il secondo è quello di prendere tutti i MakeContents dove pubblicati = True. È quindi necessario scorrere e organizzare i dati come desiderato. Ecco un buon articolo su come fare questo:

http://blog.roseman.org.uk/2010/01/11/django-patterns-part-2-efficient-reverse-lookups/


So che questa è una domanda molto vecchia, ma sto rispondendo. Poiché penso che la mia risposta possa aiutare gli altri. Ho cambiato il modello un po 'come segue. Ho usato Django 1.8.

class Make(models.Model):
    name = models.CharField(max_length=200)

class MakeContent(models.Model):
        make = models.ForeignKey(Make, related_name='makecontent')
        published = models.BooleanField()

Ho usato il seguente queryset.

Make.objects.filter(makecontent__published=True)

Spero che ti sarà d'aiuto.


Sì, penso che tu voglia

make = Make.objects.get(pk=1)
make.make_content_set.filter(published=True)

o forse

make_ids = MakeContent.objects.filter(published=True).values_list('make_id', flat=True)
makes = Make.objects.filter(id__in=make_ids)

Permettetemi di tradurre la risposta scritta di Spike in codici per i futuri spettatori. Tieni presente che ciascuna "Marca" può avere zero o più "MakeContent"

Se il richiedente significa interrogare "Crea" con ALMENO UNO "MakeContent", il cui testo è pubblicato = True, allora il secondo frammento di Jason Christa risponde alla domanda.

Lo snippet è equivalente a

makes = Make.objects.select_related().filter(makecontent__published=True).distinct()

Ma se il richiedente significa interrogare "Crea" con TUTTI i "MakeContent" di cui è stato pubblicato = True, quindi seguendo le "marche" sopra,

import operator
make_ids = [m.id for m in makes if 
    reduce(operator.and_, [c.published for c in m.makecontent_set.all()] ) 
]
makes_query = Make.objects.filter(id__in=make_ids)

contiene la query desiderata.


Concatenare i querysets in una lista è l'approccio più semplice. Se il database verrà colpito per tutti i querysets (ad es. Perché il risultato deve essere ordinato), questo non aggiungerà ulteriori costi.

from itertools import chain
result_list = list(chain(page_list, article_list, post_list))

L'uso di itertools.chain è più rapido del looping di ogni elenco e degli elementi di itertools uno ad uno, poiché itertools è implementato in C. Inoltre, consuma meno memoria rispetto alla conversione di ciascun queryset in un elenco prima della concatenazione.

Ora è possibile ordinare la lista risultante ad esempio per data (come richiesto nel commento di hasen j ad un'altra risposta). La funzione sorted() accetta comodamente un generatore e restituisce una lista:

result_list = sorted(
    chain(page_list, article_list, post_list),
    key=lambda instance: instance.date_created)

Se stai usando Python 2.4 o successivo, puoi usare attrgetter invece di un lambda. Ricordo di aver letto che era più veloce, ma non ho visto una differenza di velocità notevole per un elenco di milioni di elementi.

from operator import attrgetter
result_list = sorted(
    chain(page_list, article_list, post_list),
    key=attrgetter('date_created'))




django model filter django-queryset