Ich sehe keine gute Möglichkeit, das, was Sie vorhaben, direkt zu tun. Wenn Sie bereit sind, ein wenig De-Normalisierung zu akzeptieren, würde ich ein Pre-Save-Signal empfehlen, um Nachrichten als am Kopf zu markieren.
#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()
Dann wird Ihre Anfrage auf magische Weise einfach:
messages = Message.objects.filter(head=True).order_by('time')[0:15]
Um ganz ehrlich zu sein... der Signalhörer müsste etwas komplizierter sein als der, den ich geschrieben habe. Es gibt eine Reihe von Problemen mit verlorener Synchronisation und verlorener Aktualisierung, die meinem Ansatz innewohnen und deren Lösung von Ihrem Server abhängt (wenn es sich um einen Single-Prozessor mit mehreren Threads handelt, kann ein Python Lock
Objekt sollte ausreichen, aber wenn es sich um ein Mehrprozesssystem handelt, müssen Sie Sperren auf der Grundlage von Dateien oder Datenbankobjekten implementieren). Außerdem müssen Sie sicherlich auch einen entsprechenden Listener für das Löschsignal schreiben.
Natürlich erfordert diese Lösung das Hinzufügen von Datenbankzugriffen, aber diese sind bei der Bearbeitung und nicht bei der Ansicht, was sich für Sie lohnen könnte. Andernfalls sollten Sie vielleicht einen gröberen Ansatz in Erwägung ziehen: Nehmen Sie 30 Beiträge, gehen Sie sie in der Ansicht in einer Schleife durch, streichen Sie die, die Sie nicht anzeigen wollen, und wenn Sie noch 15 übrig haben, zeigen Sie sie an, ansonsten wiederholen Sie den Vorgang. Definitiv ein schreckliches Worst-Case-Szenario, aber vielleicht kein schrecklicher Durchschnittsfall?
Wenn Sie eine Serverkonfiguration haben, die einen einzelnen Prozess mit mehreren Threads verwendet, sollte ein Lock oder RLock ausreichen. Hier ist eine mögliche Implementierung mit nicht reentrant lock:
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()
Auch hier ist ein entsprechendes Löschsignal wichtig.
EDIT: Viele oder die meisten Serverkonfigurationen (wie z.B. Apache) arbeiten im Prefork-Modus, d.h. es laufen mehrere Prozesse ab. Der obige Code ist in diesem Fall unbrauchbar. Siehe diese Seite für Ideen, wie man mit der Synchronisierung mit Forked-Prozessen beginnen kann.