如何在Django中使用分页对一组对象进行“随机”排序?

时间:2010-10-26 09:54:16

标签: django django-models

我有一个包含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

4 个答案:

答案 0 :(得分:11)

这些究竟是多么随机?每个用户是否必须有所不同,或者仅仅是随机性的外观很重要?

如果是后者,那么您可以简单地将一个名为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,但性能问题很少。

如果记录本身不经常更改,则可以为缓存设置极高的超时。

更新

这是一种实现它的方法,它使用稍多的内存/存储但确保每个用户可以“保持”其查询集而不会出现其缓存超时的危险(假设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个查询集存储在缓存中。

答案 1 :(得分:1)

尝试使用默认的Django元选项order_by?

问一个问号“?”导致随机排序

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

答案 2 :(得分:1)

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

答案 3 :(得分:0)

您最好的做法可能是将您的查询集转换为列表,然后将其随机播放:

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

请注意,但是将查询集转换为列表会对其进行评估。如果您的Object表变大,这将成为一场性能噩梦。

如果要存储这些对象,可以创建另一个表并将用户ID与Object ID列表相关联,也可以将100个ID存储在会话cookie中。你无能为力:HTTP是无状态的,可以通过使用cookie或数据存储(更有可能是RDBS系统)来实现持久性。