Подтвердить что ты не робот

Как получить "случайный" порядок на множестве объектов с пейджингом в Django?

У меня есть модель со 100 или около того записей - клиенту хотелось бы, чтобы эти записи отображались в "случайном" порядке, но также хотели бы, чтобы там отображались страницы.

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

Итак, мой вопрос действительно должен быть: как я могу создать object_list один раз за сеанс пользователя?

4b9b3361

Ответ 1

Насколько это случайным образом? Должен ли он быть другим для каждого пользователя, или это просто внешний вид случайности, который важен?

Если это последний, вы можете просто добавить поле под названием ordering к рассматриваемой модели и заполнить его случайными целыми числами.

В противном случае, если набор записей не является небольшим (и, если он будет выгружен, я сомневаюсь в этом), то хранение отдельного случайного запроса для каждого сеанса может очень быстро стать проблемой памяти, если вы не знаете, что база пользователей очень мала, Вот одно возможное решение, которое имитирует случайность, но на самом деле создает только 5 случайных множеств:

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

В этом примере вместо создания отдельного набора запросов для каждого пользователя (в результате чего потенциально тысячи запросов в хранилище) и хранения его в request.session(менее эффективный механизм хранения, чем кеш, который может быть настроен на использование чего-то очень эффективно, как memcached), теперь у нас всего 5 запросов, хранящихся в кеше, но, надеюсь, достаточно случайный опыт для большинства пользователей. Если вы хотите больше случайности, это должно помочь увеличить значение для RANDOM_EXPERIENCES. Я думаю, вы могли бы подняться до 100 с небольшим количеством проблем с производительностью.

Если сами записи изменяются нечасто, вы можете установить чрезвычайно высокий тайм-аут для кеша.

Update

Вот способ реализовать его, который использует немного больше памяти/хранилища, но гарантирует, что каждый пользователь может "держаться" за свой запрос без опасности его тайм-аута кэша (если предположить, что 3 часа достаточно для просмотра записей).

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)

Здесь мы создаем кэшированный запрос, который не задерживается на 4 часа. Тем не менее, ключ request.session устанавливается на год, месяц, день и час, так что кто-то, кто приходит, видит текущий набор записей в течение этого часа. Любой, кто уже просмотрел запрос, сможет увидеть его как минимум еще на 3 часа (или до тех пор, пока их сеанс все еще активен) до истечения срока его действия. В лучшем случае будет храниться 5 * RANDOM_EXPERIENCES запросов в кеше.

Ответ 3

Ваш лучший способ действий, вероятно, состоит в том, чтобы преобразовать ваш запрос в список и затем перетасовать его:

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

Обратите внимание, однако, что преобразование набора запросов в список будет его оценивать. Это станет кошмаром производительности, если ваша таблица объектов станет больше.

Если вы хотите сохранить эти объекты, вы можете создать другую таблицу и связать идентификаторы пользователей со списком Идентификаторов объектов или сохранить 100 идентификаторов или около того в файле cookie сеанса. Не так много можно сделать по этому поводу: HTTP-апатрид, стойкость может быть достигнута с помощью файлов cookie или хранилища данных (скорее, система RDBS).

Ответ 4

@Решение Jordan Reiter действительно замечательно. Но есть небольшая проблема при использовании. Если запись будет обновлена, это займет некоторое время. Кроме того, он использует слишком много кеш-памяти, если количество записей велико.

Я оптимизирую его, используя только кеш столбца первичного ключа. Когда записи будут обновлены, это немедленно отразится.

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)