2015-07-31 11 views
9

Sto usando Django 1.7.7.Elementi duplicati in Django Paginate dopo "order_by` call

Mi chiedo se qualcuno lo ha provato. Questa è la mia domanda:

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

Quando provo ad allora impaginarlo

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 

ho notato che se ho rimosso il .order_by, non ottengo alcun duplicati. Ho quindi provato solo .order_by con Event.objects.all().order_by('date') che non ha dato duplicati.

Infine, ho provato questo:

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 

... e ci sono duplicati. Qualcuno può spiegare cosa sta succedendo?

Ho scavato nella sorgente Django (https://github.com/django/django/blob/master/django/core/paginator.py#L46-L55) e sembra che abbia a che fare con il modo in cui Django divide lo object_list.

Qualsiasi aiuto è apprezzato. Grazie.

Modifica: distinct() non ha alcun effetto sui duplicati. Non ci sono duplicati nel database e non penso che la query introduca duplicati ([e for e in events.iterator()] non produce duplicati). È solo quando l'impaginatore sta tagliando.

Edit2: Ecco un esempio più completo

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

Hai provato il metodo [distinct()] (https://docs.djangoproject.com/en/1.8/ref/models/querysets/#django.db.models.query.QuerySet.distinct)? – Gocht

+0

No, non l'ho fatto, ma non credo che distinti aiuterebbero. I risultati del queryset non sono duplicati. Penso che sia il modo in cui sono tirati fuori dal queryset (cioè affettare). Esecuzione 'len (set ([i.id per i in eventi])) == events.count()' produce 'True'. – truetuna

+0

Non ho capito il punto del tuo ciclo. Non puoi semplicemente usare 'event_ids = [i.id per i in eventi]'? – malisit

risposta

9

oh, sparo nel buio, ma penso che potrei so cosa sia. non ero in grado di riprodurlo in sqlite ma usando mysql. Penso mysql cercando di risolvere in una colonna che ha lo stesso valore ha restituire gli stessi risultati nel corso affettare

lo splicing impaginazione fondamentalmente fa un'istruzione SQL di SELECT ... FROM ... WHERE (date > D OR date IS NULL) ORDER BY date ASC LIMIT X OFFSET X

Ma quando la data è nullo non sono certo come MySQL lo ordina. Quindi, quando ho provato due query SQL di LIMIT 10 e LIMIT 10 OFFSET 10 ha restituito set che avevano le stesse righe, mentre LIMIT 20 produce un set unico.

puoi provare ad aggiornare il tuo ordine da order_by ('id', 'date') per fare in modo che venga ordinato per prima cosa da un campo unico e che possa ripararlo.

+0

Sembra che la stessa cosa potrebbe accadere su postgres. Ho aggiornato 'order_by' per includere un' id' e non ci sono più duplicati quando l'impaginatore taglia il queryset. Grazie mille! Ma mi piacerebbe davvero capire come funziona tutto questo. Quindi, per chiarire, è dovuto alla combinazione dell'ordinamento con NULL e LIMIT + OFFSET che sta causando la stranezza. Ho trovato questa risposta SO utile e in qualche modo correlata http://stackoverflow.com/questions/9401314/postgresql-odd-offset-limit-behavior-records-order (Vorrei darti un pollice in alto ma non lo so ho i requisiti minimi, temo). – truetuna