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.
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 ... –
Como dijiste, no es la opción perfecta, pero sigo pensando que es la mejor. Gracias – Michael
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". –