2015-07-31 6 views
9

Ich benutze Django 1.7.7.Doppelte Elemente in Django Paginate nach `order_by` Aufruf

Ich frage mich, ob jemand das erlebt hat. Dies ist meine Frage:

events = Event.objects.filter(
    Q(date__gt=my_date) | Q(date__isnull=True) 
).filter(type__in=[...]).order_by('date') 

Wenn ich versuche, dann Paginieren sie

p = Paginator(events, 10) 
p.count # Gives 91 

event_ids = [] 
for i in xrange(1, p.count/10 + 2): 
    event_ids += [i.id for i in p.page(i)] 

print len(event_ids) # Still 91 
print len(set(event_ids)) # 75 

Ich bemerkte, dass, wenn ich die .order_by entfernt, ich habe keine Duplikate erhalten. Ich versuchte dann nur .order_by mit Event.objects.all().order_by('date'), die keine Duplikate gab.

Schließlich habe ich versucht, dieses:

events = Event.objects.filter(
    Q(date__gt=my_date) | Q(date__isnull=True) 
).order_by('date') 

p = Paginator(events, 10) 
events.count() # Gives 131 
p.count # Gives 131 

event_ids = [] 
for i in xrange(1, p.count/10 + 2): 
    event_ids += [i.id for i in p.page(i)] 

len(event_ids) # Gives 131 
len(set(event_ids)) # Gives 118 

... und es gibt Duplikate. Kann mir jemand erklären, was vor sich geht?

Ich grub in die Django-Quelle (https://github.com/django/django/blob/master/django/core/paginator.py#L46-L55) und es scheint etwas damit zu tun zu haben, wie Django die object_list schneidet.

Jede Hilfe wird geschätzt. Vielen Dank.

Edit: distinct() hat keinen Einfluss auf die Duplikate. Es gibt keine Duplikate in der Datenbank und ich glaube nicht, dass die Abfrage irgendwelche Duplikate einführt ([e for e in events.iterator()] erzeugt keine Duplikate). Es ist nur, wenn der Paginator schneidet.

Edit2: Hier ist ein vollständigeres Beispiel

In [1]: from django.core.paginator import Paginator 

In [2]: from datetime import datetime, timedelta 

In [3]: my_date = timezone.now() 

In [4]: 1 events = Event.objects.filter(
      2  Q(date__gt=my_date) | Q(date__isnull=True) 
      3).order_by('date') 

In [5]: events.count() 
Out[5]: 134 

In [6]: p = Paginator(events, 10) 

In [7]: p.count 
Out[7]: 134 

In [8]: event_ids = [] 

In [9]: 1 for i in xrange(1, p.num_pages + 1): 
      2  event_ids += [j.id for j in p.page(i)] 

In [10]: len(event_ids) 
Out[10]: 134 

In [11]: len(set(event_ids)) 
Out[11]: 115 
+3

Haben Sie versucht [distinct()] (https://docs.djangoproject.com/en/1.8/ref/models/querysets/#django.db.models.query.QuerySet.distinct) Methode? – Gocht

+0

Nein, ich habe nicht, aber ich denke nicht, dass distinct helfen würde. Die Ergebnisse aus dem Abfrage-Set werden nicht dupliziert. Ich denke, es ist die Art, wie sie aus dem Queryset herausgezogen werden (d. H. Schneiden). Running 'len (set ([i.id für i in Ereignissen])) == events.count()' ergibt 'True'. – truetuna

+0

Ich konnte den Punkt Ihrer for-Schleife nicht verstehen. Kannst du nicht 'event_ids = [i.id für i in Ereignissen]' 'verwenden? – malisit

Antwort

9

oh, Schuss im Dunkeln, aber ich glaube, ich könnte wissen, was es ist. Ich konnte es nicht in sqlite, sondern mit mysql reproduzieren. Ich denke, mysql auf einer Spalte zu sortieren versuchen, die denselben Wert hat es während des Schneidens

die Paginierung Spleißen im Grunde tut eine SQL-Anweisung von SELECT ... FROM ... WHERE (date > D OR date IS NULL) ORDER BY date ASC LIMIT X OFFSET X

die gleichen Ergebnisse Rückkehr Aber wenn Datum null ist, ich bin nicht Sicher, wie MySQL es sortiert. Als ich also zwei SQL-Abfragen von LIMIT 10 und LIMIT 10 OFFSET 10 ausprobierte, gab es Sätze zurück, die dieselben Reihen hatten, während LIMIT 20 einen eindeutigen Satz erzeugte.

Sie können versuchen, Ihre order_by auf order_by ('ID', 'Datum') zu aktualisieren, um es zuerst nach einem eindeutigen Feld sortieren zu lassen und es kann es beheben.

+0

Es sieht so aus, als würde das Gleiche bei Postgres passieren. Ich habe 'order_by' aktualisiert, um eine' id' zu enthalten, und es gibt keine Dubletten mehr, wenn der Paginator das Abfrage-Set schneidet. Vielen Dank! Aber ich würde wirklich gerne verstehen, wie das alles funktioniert. Also, um es zu verdeutlichen, ist es wegen der Kombination der Sortierung mit NULLs und LIMIT + OFFSET, die die Seltsamkeit verursacht. Ich fand diese SO Antwort hilfreich und etwas verwandt http://stackoverflow.com/questions/9401314/postgresql-odd-offset-limit-behavior-records-order (Ich würde Ihnen einen Daumen hoch, aber ich nicht die Mindestanforderungen erfüllen, fürchte ich). – truetuna