2011-03-01 9 views
9

¿hay alguna manera de paginar un rawqueryset usando la paginación incorporada de django? cuando lo lanzo a una lista, arroja un error en mi cara ... TypeError: string esperado o objeto Unicode, NoneType encontrado. ¿Hay alguna forma de evitar esto?paginación django y RawQuerySet

Respuesta

14

me las arreglé para lograrlo utilizando la siguiente:

paginator = Paginator(files, 12) 
paginator._count = len(list(files)) 

El código en django.core.paginator.py:

  • controles para comprobar _count se establece
  • si no se trata a continuación
  • ejecutar .count() que no existe
  • si no, entonces prueba plain len

len en un raw_queryset no obra, sino convertir el objeto paginador real para encontrar una lista de obras para mí en Django 1.3

+7

Obviamente len (lista (archivos)) es muy ineficiente de memoria para grandes conjuntos de consulta primas. Dado que conoce la consulta que se ejecutó, podría ejecutar otra consulta con COUNT (*) en su lugar y asignarla al paginator._count siempre que la cantidad de resultados no cambie entre cada consulta. Alternativamente, diferentes DBMS tienen formas en las que puede insertar el número total de filas en cada fila de la consulta si los resultados de la consulta cambian constantemente. – Chris

+0

Desafortunadamente, [RawQuerySet .__ getitem__()] (https://code.djangoproject.com/browser/django/trunk/django/db/models/query.py?rev=17381#L1517) lista de llamadas (self) de todos modos - por lo tanto, se cargará totalmente en la memoria tan pronto como llame a 'paginator.get_page()'. Para evitar eso, creo que tendrías que subclasar RawQuerySet y asegurarte de que tu SQL en bruto tiene LIMIT/OFFSET, e incluso entonces, vas a perder el caché de resultados de un queryset normal, para acceder a qs [0] dos veces golpeará el DB dos veces. – AdamKG

+0

Sí, se golpeará dos veces. Acabo de descubrirlo por mí también. Por lo tanto, es mejor que escriba sql MUY eficiente para ese queryset bruto y en lugar de pasar el queryset bruto a len (list()) y al paginator primero evalúe el queryset haciendo un looping sobre él. Como l = [elemento para el elemento en queryset] y luego pase ese l a ambos paginator y len (l). Esto le da solo una llamada a la base de datos. –

4

Puede configurar manualmente el atributo de cuenta para su objeto RawQuerySet:

items = Item.objects.raw("select * from appitem_item") 

def items_count(): 
    cursor = connection.cursor() 
    cursor.execute("select count(*) from appitem_item") 
    row = cursor.fetchone() 
    return row[0] 

items.count = items_count 

para @Rockallite

>>> class A(): 
... def b(self): 
...  print 'from b' 
... 
>>> 
>>> (A()).b() 
from b 
>>> def c(): 
... print 'from c' 
... 
>>> a = A() 
>>> a.b = c 
>>> a.b() 
from c 
+0

django.core.paginator.Paginator busca el método 'count()'. Entonces, configurar la propiedad 'count' no funciona. – Rockallite

+0

¿Por qué, lo has probado? Por supuesto, es un truco, pero lo usé. Quiero decir que en Python puedes reemplazar un método por otro. –

+0

Rockallite, agregué el código para ilustrar cómo funciona este truco –

-2
qs.filter(**pfilter).distinct().extra(select={'test': 'COALESCE(`psearch_program`.`eu_price`, 999999999)'}).extra(order_by=['test']) 
+0

-1: explica qué se está haciendo, con qué es compatible, etc. –