Non vedo alcun modo per fare ciò che stai cercando di fare direttamente. Se sei disposto ad accettare un po 'di de-normalizzazione, ti consiglierei un segnale di pre-salvataggio per contrassegnare i messaggi come se fossero alla testa.
#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()
Poi la query diventa magicamente semplice:
messages = Message.objects.filter(head=True).order_by('time')[0:15]
per essere onesti ... l'ascoltatore segnale sarebbe dovuto essere un po 'più complicato di quello che ho scritto. Esistono numerosi problemi di sincronizzazione/perdita persi inerenti al mio approccio, le cui soluzioni variano a seconda del server (se è single-process, multi-threaded, quindi un oggetto python Lock
dovrebbe farvi passare, ma se è multi-elaborato, quindi sarà davvero necessario implementare il blocco basato su file o oggetti di database). Inoltre, dovrai anche scrivere un listener del segnale di cancellazione corrispondente.
Ovviamente questa soluzione comporta l'aggiunta di alcuni hit del database, ma sono in modifica anziché in visualizzazione, il che potrebbe essere utile per voi. Altrimenti, prendi in considerazione un approccio più rozzo: prendi 30 piani, passa attraverso la vista, elimina quelli che non verranno visualizzati e, se ne hai 15, mostrali, altrimenti ripeti. Decisamente un pessimo scenario terribile, ma forse non un caso medio terribile?
Se si disponeva di una configurazione del server che utilizzava un singolo processo multi-thread, un lucchetto o una RLock dovrebbero fare il trucco.Ecco una possibile implementazione con blocco non rientro:
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()
Anche in questo caso, un segnale di eliminazione corrispondente è fondamentale.
MODIFICA: molte o la maggior parte delle configurazioni del server (come Apache) eseguiranno il prefork, il che significa che ci sono diversi processi in corso. Il codice sopra sarà inutile in quel caso. Vedi this page per le idee su come iniziare a sincronizzare con i processi biforcati.
Penso che questo sia l'approccio generale che dovresti prendere, sebbene tu possa sostituire gli hit del database memorizzando last_source in memcached; ci sarebbe ancora una potenziale condizione di competizione in condizioni di concorrenza pesante, ma se non dovesse essere perfetta al 100% ... –
Come hai detto, non è l'opzione perfetta, ma continuo a pensare che sia la migliore. Grazie – Michael
Inoltre ... Suppongo che mi sia sempre piaciuto usare i segnali ... ma sovrascrivere il metodo di salvataggio potrebbe fare la stessa cosa. Alcuni puristi stilisti sostengono che è "la cosa giusta". –