2010-10-26 5 views
12

Ich habe ein Modell mit 100 oder so Einträge - der Client möchte, dass diese Einträge in einer "zufälligen" Reihenfolge angezeigt werden, möchten aber auch dort paging.Wie kann man in Django eine "zufällige" Reihenfolge für eine Reihe von Objekten mit Paging haben?

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) 
    .... 

So sollte meine Frage wirklich sein - wie kann ich meine object_list einmal pro Benutzersitzung erstellt?

Antwort

10

Genau wie zufällig müssen diese sein? Muss es für jeden Benutzer anders sein, oder ist es nur die Erscheinung der Zufälligkeit, die wichtig ist?

Wenn es Letzteres ist, dann können Sie einfach ein Feld namens ordering zu dem fraglichen Modell hinzufügen und es mit zufälligen Ganzzahlen füllen.

Sonst, wenn das Recordset klein ist (und wenn es paged ist, bezweifle ich es), dann Speichern eines separaten zufälligen Abfrage-Set für jede Sitzung kann ein Speicherproblem sehr schnell werden, wenn Sie wissen, dass die Benutzerbasis sehr ist klein. Hier ist eine mögliche Lösung, die Zufälligkeit, sondern in Wirklichkeit nachahmt erzeugt nur 5 zufällige Mengen:

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) 
    .... 

In diesem Beispiel anstelle einen separaten queryset für jeden Benutzer zu schaffen (was zu potentiell tausende querysets in Speicher), und es speichert in request.session (ein weniger effizienter Speichermechanismus als Cache, der so eingestellt werden kann, dass er etwas sehr Effizientes verwendet, wie Memcached), haben wir jetzt nur 5 Abfragesätze im Cache gespeichert, aber hoffentlich eine ausreichend zufällige Erfahrung für die meisten Benutzer. Wenn Sie mehr Zufälligkeit wünschen, sollten Sie den Wert für RANDOM_EXPERIENCES erhöhen. Ich denke, dass man mit wenigen Performanceproblemen bis zu 100 hochgehen könnte.

Wenn sich die Datensätze selbst nur selten ändern, können Sie eine extrem hohe Zeitüberschreitung für den Cache festlegen.

aktualisiert

Hier ist ein Weg, es zu implementieren, die etwas mehr Speicher/Speicher verwendet wird sichergestellt, sondern dass jeder Benutzer ihre queryset lange genug, um ohne Gefahr seines Cache Timeout (unter der Annahme, dass 3 Stunden „on hold“ kann um die Aufzeichnungen zu sehen).

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) 

Hier erstellen wir ein zwischengespeichertes Abfrage-Set, das für 4 Stunden nicht ausläuft. Der Schlüssel request.session wird jedoch auf das Jahr, den Monat, den Tag und die Stunde festgelegt, sodass ein ankommender Benutzer einen aktuellen Datensatz für diese Stunde sieht. Jeder, der das Abfrage-Set bereits angezeigt hat, kann es mindestens noch 3 Stunden (oder solange seine Sitzung noch aktiv ist) sehen, bevor es abläuft. Es werden höchstens 5 * RANDOM_EXPERIENCES-Abfragesätze im Cache gespeichert.

+0

nice one - nicht den Cache wie zuvor verwendet - nur um Ansichten zu cachen. scheint einen Sinn zu ergeben. Und du hast Recht - die Zufälligkeit ist nur für den Schein, nicht wirklich zufällig. Vielen Dank. –

0

Ihre beste Vorgehensweise ist wahrscheinlich queryset auf eine Liste zu konvertieren und dann mischt es:

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

Anmerkung Sie jedoch, dass eine queryset auf eine Liste Umwandlung wird es zu bewerten. Dies wird zu einem Alptraum, wenn Ihre Objekttabelle größer wird.

Wenn Sie diese Objekte speichern möchten, können Sie entweder eine andere Tabelle erstellen und Benutzer-IDs mit einer Liste von Objekt-IDs verknüpfen oder die 100 IDs in einem Sitzungscookie speichern. Es gibt nicht viel, was Sie dagegen tun können: HTTP ist zustandslos, Persistenz kann durch Verwendung von Cookies oder eines Datenspeichers (ein RDBS-System ist wahrscheinlich) erreicht werden.

1

Verwenden Sie die Standard-Django-Meta-Option order_by?

Setzen Sie ein Fragezeichen "?" Ergebnisse werden in zufälliger Reihenfolge

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

+1

Die Sache mit diesem Ansatz ist (mit der eingebauten Paginierung zumindest) die Zufälligkeit berücksichtigt nicht, was auf anderen Seiten war. So etwas, was ich auf der ersten Seite gesehen habe, könnte immer noch auf anderen Seiten erscheinen. – wasabigeek

0

@Jordan Reiter-Lösung wirklich toll. Aber es gibt ein kleines Problem, wenn man es benutzt. Wenn der Datensatz aktualisiert wird, dauert es nur einmal. Außerdem verwendet es zu viel Cachespeicherplatz, wenn die Anzahl der Datensätze groß ist.

Ich optimieren es, indem Sie nur die Primärschlüsselspalte zwischenspeichern. Wenn Datensätze aktualisiert werden, wird dies sofort wirksam.

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) 
Verwandte Themen