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

iszxjhcz  于 2023-06-25  发布在  Go
关注(0)|答案(5)|浏览(125)

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

x4shl7ld

x4shl7ld1#

这到底有多随机对于每个用户来说,它必须是不同的吗?或者仅仅是随机性的外观很重要?
如果是后者,那么您可以简单地将一个名为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查询集。

hkmswyz6

hkmswyz62#

尝试使用默认的Django meta选项order_by?
打个问号“?“导致随机排序
https://docs.djangoproject.com/en/1.3/ref/models/options/#ordering

qpgpyjmq

qpgpyjmq3#

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

c90pui9n4#

最好的做法可能是将查询集转换为列表,然后对其进行 Shuffle :

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

但是请注意,将查询集转换为列表将对其求值。如果Object表变得更大,这将成为性能的噩梦。
如果要存储这些对象,可以创建另一个表,并将用户id与对象id列表相关联,或者将100个左右的id存储在会话cookie中。对此,你能做的并不多:HTTP是无状态的,持久化可以通过使用cookie或数据存储(更可能是RDBS系统)来实现。

t5zmwmid

t5zmwmid5#

如果您只需要伪随机性,手动调用setseed就足够了(并且可以避免任何内存/性能问题):

from django.db import connection

seed = qs.count()
with connection.cursor() as cursor:
    cursor.execute("SELECT setseed(%s);" % seed)
qs.order_by('?')

从此以后,你就可以像往常一样继续进行分页了。尽管随机性只会在查询集中的对象数量发生变化后才发生变化。这可以是想要的(例如,人们可以说“第二页上的第二个条目”,这在真实的的随机性下是完全不可能的)。你也可以尝试采用另一种种子方法,例如seed = timezone.now().strftime("%m"),每个月都有新的随机性。

相关问题