2010-10-26 11 views
12

Tengo un modelo con más o menos 100 entradas: el cliente desea que estas entradas aparezcan en un orden 'aleatorio', pero también quiere que se coloque allí.¿Cómo hacer un pedido "aleatorio" en un conjunto de objetos con paginación en Django?

def my_view(request): 
    object_list = Object.objects.all().order_by('?') 
    paginator = Paginator(object_list, 10) 
    page = 1 # or whatever page we have 
    display_list = paginator.page(page) 
    .... 

Así que mi pregunta en realidad debería ser - ¿Cómo puedo tener mi object_list creado una vez por sesión de usuario?

Respuesta

10

Exactamente, ¿qué tan aleatorio deben ser? ¿Tiene que ser diferente para cada usuario, o es simplemente la apariencia de la aleatoriedad que es importante?

Si es el último, entonces simplemente puede agregar un campo llamado ordering al modelo en cuestión, y rellenarlo con enteros aleatorios. De lo contrario, a menos que el conjunto de registros sea pequeño (y, dado que está siendo paginado, lo dudo), almacenar un conjunto de consulta aleatorio para cada sesión podría convertirse en un problema de memoria muy rápidamente a menos que sepa que la base de usuarios es muy pequeña. Aquí es una posible solución que imita aleatoriedad pero en realidad sólo crea 5 juegos al azar:

import random 
from django.core import cache 
RANDOM_EXPERIENCES=5 

def my_view(request): 
    if not request.session.get('random_exp'): 
     request.session['random_exp']=random.randrange(0,RANDOM_EXPERIENCES) 
    object_list = cache.get('random_exp_%d' % request.session['random_exp']) 
    if not object_list: 
     object_list = list(Object.objects.all().order_by('?')) 
     cache.set('random_exp_%d' % request.session['random_exp'], object_list, 100) 
    paginator = Paginator(object_list, 10) 
    page = 1 # or whatever page we have 
    display_list = paginator.page(page) 
    .... 

En este ejemplo, en lugar de crear una queryset independiente para cada usuario (que resulta en potencialmente miles de QuerySets en el almacenamiento) y almacenarla en request.session (un mecanismo de almacenamiento menos eficiente que el caché, que puede configurarse para usar algo muy eficiente, como memcached), ahora solo tenemos 5 querysets almacenados en el caché, pero con suerte una experiencia suficientemente aleatoria para la mayoría de los usuarios. Si quieres más aleatoriedad, aumentar el valor de RANDOM_EXPERIENCES debería ser útil. Creo que probablemente puedas subir hasta 100 con pocos problemas de rendimiento.

Si los registros propios cambian con poca frecuencia, puede establecer un tiempo de espera extremadamente alta para la memoria caché.

actualización

Aquí está una manera de ponerlo en práctica que utiliza poco más de memoria/almacenamiento, pero se asegura de que cada usuario puede "aferrarse" a su queryset sin peligro de su tiempo caché a cabo (suponiendo que 3 horas es lo suficientemente largo para mirar los registros).

import datetime 

... 

    if not request.session.get('random_exp'): 
     request.session['random_exp']="%d_%d" % ( 
      datetime.datetime.strftime(datetime.datetime.now(),'%Y%m%dH'), 
      random.randrange(0, RANDOM_EXPERIENCES) 
     ) 
    object_list = cache.get("random_exp_%s" % request.session['random_exp']) 
    if not object_list: 
     object_list = list(Object.objects.all().order_by('?')) 
     cache.set(cache_key, "random_exp_%s" % request.session['random_exp'], 60*60*4) 

Aquí creamos un conjunto de consultas en caché que no excede el tiempo de espera durante 4 horas. Sin embargo, la clave request.session se establece en el año, el mes, el día y la hora para que alguien que ingrese vea un juego de registros actual para esa hora. Cualquier persona que ya haya visto el conjunto de consulta podrá verlo durante al menos otras 3 horas (o mientras su sesión esté activa) antes de que caduque. Como máximo, habrá 5 * conjuntos de consultas RANDOM_EXPERIENCES almacenados en caché.

+0

nice one - no se usó el caché como antes - solo para vistas de caché. parece tener sentido Y tienes razón: la aleatoriedad es solo por las apariencias, no es verdaderamente aleatoria. Gracias. –

0

Su mejor curso de acción es, probablemente, a convertir su queryset a una lista, y luego barájalo:

from random import shuffle 
object_list = list(object_list) 
shuffle(object_list) 
... continue with pagination ... 

hacer la nota, sin embargo, que la conversión de un conjunto de consultas a una lista de lo evaluará. Esto se convertirá en una pesadilla de rendimiento si su tabla Object se hace más grande.

Si desea almacenar esos objetos, puede crear otra tabla y asociar identificadores de usuario con una lista de Id. De objeto, o almacenar los 100 ids más o menos en una cookie de sesión. No hay mucho que pueda hacer al respecto: HTTP es sin estado, la persistencia se puede lograr mediante el uso de cookies o un almacén de datos (un sistema RDBS es más probable).

1

Intenta usar la metaopción predeterminada de Django order_by?

Poniendo un signo de interrogación "?" los resultados en orden aleatorio

https://docs.djangoproject.com/en/1.3/ref/models/options/#ordering

+1

Lo que pasa con este enfoque es (con la paginación incorporada al menos) que la aleatoriedad no tiene en cuenta lo que estaba en otras páginas. Entonces, algo que vi en la primera página podría aparecer en otras páginas. – wasabigeek

0

solución de @Jordan Reiter realmente grande. Pero hay un pequeño problema cuando lo usas. Si el registro se actualiza, tomará un tiempo en solitario para efectuarlo. Además, usa demasiado espacio de caché si el recuento de registros es grande.

Lo optimizo solo con el caché de la columna de la clave principal. Cuando se actualicen los registros, tendrá efecto de inmediato.

import random 
from django.core import cache 
from django.core.paginator import Paginator 
RANDOM_EXPERIENCES=5 

if not request.session.get('random_exp'): 
    request.session['random_exp']=random.randrange(0,RANDOM_EXPERIENCES) 
id_list = cache.get('random_exp_%d' % request.session['random_exp']) 
if not id_list: 
    id_list = [object['id'] for object in Object.objects.values('id').all().order_by('?')] 
    cache.set('random_exp_%d' % request.session['random_exp'], id_list, 60*60*4) 
paginator = Paginator(id_list, 9) 
page = 1 # or whatever page we have 
display_id_list = paginator.page(page) 
object_list = Object.objects.filter(id__in=display_id_list) 
Cuestiones relacionadas