2009-03-12 14 views
5

Para un sitio web implementada en Django/Python tenemos el siguiente requisito:de paginación en función de agrupación de artículos en Django

En una página vista hay 15 mensajes por paginación web que se indica. Cuando hay más dos o más mensajes de la misma fuente, que se suceden en la vista, deben agruparse.

Tal vez no está claro, pero con las siguientes exemple podría ser:

un ejemplo (con 5 mensajes en una página esta vez):

Message1 Source1 
    Message2 Source2 
    Message3 Source2 
    Message4 Source1 
    Message5 Source3 
    ... 

Esto debe ser mostrada como:

Message1 Source1 
Message2 Source2 (click here to 1 more message from Source2) 
Message4 Source1 
Message5 Source3 
Message6 Source2 

De modo que en cada página se muestra un número fijo de elementos en la página, donde algunos se han reagrupado.

Nos preguntamos cómo podemos crear una consulta Django o MySQL para consultar estos datos de una manera óptima y fácil. Tenga en cuenta que se utiliza la paginación y que los mensajes se ordenan por tiempo.

PS: Creo que no hay una solución simple para esto debido a la naturaleza de SQL, pero a veces problemas complejos pueden ser fácilmente resuelto tengo aunque no es perfecto, la solución de la plantilla de sólo

Respuesta

3

No veo ninguna gran manera de hacer lo que está tratando de hacer directamente. Si está dispuesto a aceptar una pequeña desnormalización, recomendaría una señal de pre-guardado para marcar los mensajes como encabezados.

#In your model 
head = models.BooleanField(default=True) 

#As a signal plugin: 
def check_head(sender, **kwargs): 
    message = kwargs['instance'] 
    if hasattr(message,'no_check_head') and message.no_check_head: 
     return 
    previous_message = Message.objects.filter(time__lt=message.time).order_by('-time')[0] 
    if message.source == previous_message.source: 
     message.head = False 
    next_message = Message.objects.filter(time__gt=message.time).order_by('time')[0] 
    if message.source == next_message.source: 
     next_message.head = False 
     next_message.no_check_head 
     next_message.save() 

Luego, su consulta se convierte mágicamente sencillo:

messages = Message.objects.filter(head=True).order_by('time')[0:15] 

para ser sincero ... el que escucha la señal tendría que ser un poco más complicado que el que escribí. Hay una serie de problemas de perdida de sincronización/actualización perdida inherentes a mi enfoque, cuyas soluciones variarán dependiendo de su servidor (si es de procesamiento único, multiproceso, entonces un objeto python Lock debería pasar, pero si es multi-procesado, entonces realmente necesitará implementar el bloqueo basado en archivos u objetos de base de datos). Además, seguramente también tendrá que escribir un oyente de señal de eliminación correspondiente.

Obviamente, esta solución implica agregar algunas visitas a la base de datos, pero están en edición y no en vista, lo que puede valer la pena para usted. De lo contrario, tal vez considere un enfoque más crudo: agarre 30 historias, recorra la vista, elimine las que no mostrará, y si le quedan 15, muéstrelas, de lo contrario repita. Definitivamente un horrible peor de los casos, pero tal vez no es un terrible caso promedio?

Si tenía una configuración de servidor que utilizaba un único proceso que tiene varios subprocesos, un bloqueo o RLock debería ser el truco.He aquí una posible implementación con el bloqueo no reentrante:

import thread 
lock = thread.allocate_lock() 
def check_head(sender, **kwargs): 
    # This check must come outside the safe zone 
    # Otherwise, your code will screech to a hault 
    message = kwargs['instance'] 
    if hasattr(message,'no_check_head') and message.no_check_head: 
     return 
    # define safe zone 
    lock.acquire() 
    # see code above 
    .... 
    lock.release() 

Una vez más, una señal de borrado correspondiente es fundamental también.

EDITAR: Muchas o la mayoría de las configuraciones de servidor (como Apache) se realizarán antes, lo que significa que hay varios procesos en curso. El código anterior será inútil en ese caso. Consulte this page para obtener ideas sobre cómo comenzar a sincronizar con procesos bifurcados.

+0

Creo que este es el enfoque general que tendría que tomar, aunque podría reemplazar las visitas de la base de datos almacenando last_source en memcached; todavía habría una condición de carrera potencial bajo concurrencia pesada, pero si no tenía que ser 100% perfecto ... –

+0

Como dijiste, no es la opción perfecta, pero sigo pensando que es la mejor. Gracias – Michael

+0

También ... Supongo que siempre me ha gustado usar señales ... pero anulando el método de guardar podría hacer lo mismo. Algunos puristas estilísticos argumentarían que eso es "lo correcto". –

1

un simple, para esto. En la plantilla, puede reagrupar los registros con la etiqueta de plantilla regroup. Después de la reagrupación puede ocultar registros sucesivos de la misma fuente:

{% regroup records by source as grouped_records %} 
{% for group in grouped_records %} 
    {% for item in group.list %} 
    <li{% if not forloop.first %} style="display:none"{% endif %}> 
     {{ item.message }} {{ iterm.source }} 
     {% if forloop.first %} 
     {% ifnotequal group.list|length 1 %} 
      <a href="#" onclick="...">Show more from the same source...</a> 
     {% endifnotequal %}   
     {% endif %} 
    </li> 
    {% endfor %} 
{% endfor %} 

Esto sería perfecto si no fuera por una cosa: La paginación. Si quiere mostrar 15 elementos por página, y en una página, los primeros cinco son de una fuente, los siguientes cinco de otra y los últimos cinco, otra, solo habrá tres elementos visibles en la página.

Cuestiones relacionadas