2015-11-07 11 views
7

Ho un modello libro e un modello di rating,interrogazione Django SQL duplicato n volte

class Book(models.Model): 
    title = models.CharField(max_length=255) 
    slug = AutoSlugField(unique=True, populate_from='title') 
    description = models.TextField() 
    # more fields 

class Rating(models.Model): 
    book = models.ForeignKey('library.Book') 
    score = models.DecimalField(max_digits=2, decimal_places=1) 

la query,

books = {'books': Book.objects.filter(pk__in=Rating.objects.all().order_by('-score' 
      ).values_list('book__id', flat=True))[:10] } 

template,

{% for i in books %} 
    {{ i.title }}, {{ i.rating_set.all.first.score }} <br/> 
{% endfor %} 

rende il modello al modello, ma la barra degli strumenti di debug di Django mostra come n volte duplicato dove n è il numero di oggetti nell'elenco. quando uso la cache di queryset, è normale.

enter image description here

che cosa sta succedendo dietro, come posso risolvere questo problema?

grazie.

risposta

4

Leggi informazioni su select_related e prefetch_related.

Book.objects.filter(pk__in=Rating.objects.all().order_by('-score').values_list('book__id', flat=True)).preferch_related('rating_set')[:10] 

Nel modello che si desidera accedere al voto libro {{ i.rating_set.all.0.score }}. Senza select_related/prefetch_related Django in ogni riga crea una nuova query. Con prefetch_related Django ha effettuato 1 query e ha ottenuto tutte le valutazioni.

Nel tuo caso il problema potrebbe essere in .first..

+0

non cambia nulla, la sua qualcosa che si è verificato quando il modello nella parte '{{}} i.rating_set.all.first.score' – Rivadiz

+0

come posso risolvere questo? – Rivadiz

+1

cambiato 'prima' a' 0' e ora va bene – Rivadiz

5

non prova, ma si dovrebbe assolutamente prelettura rating_set di non fare ulteriore colpo di database per ogni libro per trovare il loro punteggio più alto:

rated_books = Rating.objects.all().order_by('-score').values_list('book', flat=True) 
books = Book.objects.prefetch_related('rating_set').filter(pk__in=rated_books)[:10] 

Nel modello, ho anche il sospetto .first e .all in quanto possono causare un colpo di db aggiuntivo. Inoltre, non è necessario chiamare lo .first perché sappiamo già che questi libri con valore hanno almeno un oggetto di valutazione.

{% for book in books %} 
    {{ book.title }}, {{ book.rating_set.0.score }} <br/> 
{% endfor %}