【发布时间】:2010-12-16 10:11:12
【问题描述】:
如何使用 Django 获得两个不同的随机记录?我已经看到有关如何获得一个的问题,但我需要获得两个随机记录,它们必须不同。
【问题讨论】:
标签: python django django-models random sql-order-by
如何使用 Django 获得两个不同的随机记录?我已经看到有关如何获得一个的问题,但我需要获得两个随机记录,它们必须不同。
【问题讨论】:
标签: python django django-models random sql-order-by
其他答案建议的order_by('?')[:2] 解决方案实际上对于具有大量行的表来说是一件非常糟糕的事情。它会产生一个ORDER BY RAND() SQL 查询。例如,下面是 mysql 处理的方式(其他数据库的情况并没有太大的不同)。假设您的表有 10 亿行:
ORDER BY RAND(),它需要一个RAND() 列进行排序。注意:只是为了一点额外的肉汁,请注意 mysql 最初会尝试在 RAM 中创建该临时表。当这用尽时,它会暂停一切以将整个内容复制到磁盘,因此您几乎在整个过程中都会遇到额外的 I/O 瓶颈。
怀疑者应该查看生成的查询以确认它是ORDER BY RAND(),然后在 Google 上搜索“order by rand()”(带引号)。
更好的解决方案是将一个 非常 昂贵的查询换成三个便宜的查询(限制/偏移而不是 ORDER BY RAND()):
import random
last = MyModel.objects.count() - 1
index1 = random.randint(0, last)
# Here's one simple way to keep even distribution for
# index2 while still gauranteeing not to match index1.
index2 = random.randint(0, last - 1)
if index2 == index1: index2 = last
# This syntax will generate "OFFSET=indexN LIMIT=1" queries
# so each returns a single record with no extraneous data.
MyObj1 = MyModel.objects.all()[index1]
MyObj2 = MyModel.objects.all()[index2]
【讨论】:
count,因此这也可能相当昂贵。一般来说,获取随机记录是一件相当昂贵的事情。
如果您在 ORM 中指定随机运算符,我很确定它会给您两个不同的随机结果,不是吗?
MyModel.objects.order_by('?')[:2] # 2 random results.
【讨论】:
为未来的读者。
获取所有记录的id列表:
my_ids = MyModel.objects.values_list('id', flat=True)
my_ids = list(my_ids)
然后从上面所有的id中随机挑选n个id:
n = 2
rand_ids = random.sample(my_ids, n)
并获取这些 id 的记录:
random_records = MyModel.objects.filter(id__in=rand_ids)
【讨论】:
my_ids = list(my_ids),否则我会从random.sample 得到TypeError。我还实现了一种方法来处理 my_ids 的记录少于我的样本大小,通过执行 sample_size = min(len(my_ids), 10)。
Object.objects.order_by('?')[:2]
这将返回两个随机排序的记录。你可以添加
distinct()
如果您的数据集中存在具有相同值的记录。
【讨论】:
distinct(),您最终可能只有 1 个值。
关于从一个序列中采样n个随机值,可以使用随机库,
random.Random().sample(range(0,last),2)
将从序列元素中获取 2 个随机样本,从 0 到 last-1
【讨论】:
from django.db import models
from random import randint
from django.db.models.aggregates import Count
class ProductManager(models.Manager):
def random(self, count=5):
index = randint(0, self.aggregate(count=Count('id'))['count'] - count)
return self.all()[index:index + count]
您可以获得不同数量的对象。
【讨论】:
class ModelName(models.Model):
# Define model fields etc
@classmethod
def get_random(cls, n=2):
"""Returns a number of random objects. Pass number when calling"""
import random
n = int(n) # Number of objects to return
last = cls.objects.count() - 1
selection = random.sample(range(0, last), n)
selected_objects = []
for each in selection:
selected_objects.append(cls.objects.all()[each])
return selected_objects
【讨论】: