我有一个模特代表我在我网站上出现的画作。在主要网页上,我想展示一些:最新的,大多数时间没有访问过的,最受欢迎的一个和随机的一个。
我正在使用Django 1.0.2。
虽然前三个使用django模型很容易拉,但最后一个(随机)给我带来了一些麻烦。我可以在我的视图中对它进行编码,如下所示:
number_of_records = models.Painting.objects.count()
random_index = int(random.random()*number_of_records)+1
random_paint = models.Painting.get(pk = random_index)
在我看来,它看起来并不像我想要的东西 - 这完全是数据库抽象的一部分,应该在模型中。此外,在这里我需要处理删除的记录(然后所有记录的数量不会覆盖我所有可能的键值),可能还有很多其他的东西。
我可以做任何其他选择,最好以某种方式在模型抽象中?
使用order_by('?')
将在生产的第二天终止数据库服务器。更好的方法就像Getting a random row from a relational database中描述的那样。
from django.db.models.aggregates import Count
from random import randint
class PaintingManager(models.Manager):
def random(self):
count = self.aggregate(count=Count('id'))['count']
random_index = randint(0, count - 1)
return self.all()[random_index]
这是强烈推荐的 qazxsw poi
因为使用django orm做这样的事情,如果你有大数据表会使你的db服务器特别生气:
解决方案是提供模型管理器并手动编写SQL查询;)
更新:
另一个解决方案适用于任何数据库后端甚至非相关的后端,而无需编写自定义Getting a random row from a relational database。 ModelManager
您可能希望使用用于对任何迭代器进行采样的Getting Random objects from a Queryset in Django,尤其是当您计划对多个项目进行采样以创建样本集时。 @MatijnPieters和@DzinX对此有很多想法:
same approach
一个更简单的方法是简单地过滤到感兴趣的记录集,并使用def random_sampling(qs, N=1):
"""Sample any iterable (like a Django QuerySet) to retrieve N random elements
Arguments:
qs (iterable): Any iterable (like a Django QuerySet)
N (int): Number of samples to retrieve at random from the iterable
References:
@DZinX: https://stackoverflow.com/a/12583436/623735
@MartinPieters: https://stackoverflow.com/a/12581484/623735
"""
samples = []
iterator = iter(qs)
# Get the first `N` elements and put them in your results list to preallocate memory
try:
for _ in xrange(N):
samples.append(iterator.next())
except StopIteration:
raise ValueError("N, the number of reuested samples, is larger than the length of the iterable.")
random.shuffle(samples) # Randomize your list of N objects
# Now replace each element by a truly random sample
for i, v in enumerate(qs, N):
r = random.randint(0, i)
if r < N:
samples[r] = v # at a decreasing rate, replace random items
return samples
选择任意多个:
random.sample
请注意,您应该有一些代码来验证from myapp.models import MyModel
import random
my_queryset = MyModel.objects.filter(criteria=True) # Returns a QuerySet
my_object = random.sample(my_queryset, 1) # get a single random element from my_queryset
my_objects = random.sample(my_queryset, 5) # get five random elements from my_queryset
不为空;如果第一个参数包含的元素太少,my_queryset
会返回random.sample
。
嗨我需要从查询集中选择一个随机记录,我需要报告的长度(即网页生成描述的项目和所述记录留下)
ValueError: sample larger than population
花了一半的时间(0.7s vs 1.7s):
q = Entity.objects.filter(attribute_value='this or that')
item_count = q.count()
random_item = q[random.randomint(1,item_count+1)]
我猜它避免在选择随机条目之前拉下整个查询,并使我的系统对于重复访问的页面响应足够,以便用户希望看到item_count倒计时。
我得到了非常简单的解决方案,制作自定义管
item_count = q.count()
random_item = random.choice(q)
然后添加模型:
class RandomManager(models.Manager):
def random(self):
return random.choice(self.all())
现在,您可以使用它:
class Example(models.Model):
name = models.CharField(max_length=128)
objects = RandomManager()
如果你有一个表,其中主键是一个没有间隙的顺序整数,那么下面的方法应该工作:
Example.objects.random()
此方法比遍历表的所有行的其他方法更有效。虽然它确实需要两个数据库查询,但两者都是微不足道的。此外,它很简单,不需要定义任何额外的类。但是,它的适用性仅限于具有自动递增主键的表,其中行从未删除,因此ID序列中没有间隙。
如果已删除行以使其成为间隙,则在重试之前,此方法仍然可以工作,直到随机选择现有主键。
import random
max_id = MyModel.objects.last().id
random_id = random.randint(0, max_id)
random_obj = MyModel.objects.get(pk=random_id)
如果使用MySQL(不了解其他数据库),即使对于中型表,order_by('?')[:N]的解决方案也非常慢。
order_by('?')[:N]
将被翻译为SELECT ... FROM ... WHERE ... ORDER BY RAND() LIMIT N
查询。
这意味着对于表中的每一行,将执行RAND()函数,然后将根据此函数的值对整个表进行排序,然后返回前N个记录。如果你的桌子很小,这很好。但在大多数情况下,这是一个非常慢的查询。
我写了一个简单的函数,即使id有漏洞(某些行被删除)也能正常工作:
def get_random_item(model, max_id=None):
if max_id is None:
max_id = model.objects.aggregate(Max('id')).values()[0]
min_id = math.ceil(max_id*random.random())
return model.objects.filter(id__gte=min_id)[0]
几乎在所有情况下,它都比order_by('?')快。
你可以在你的模型上创建一个manager来做这种事情。要首先了解经理是什么,Painting.objects
方法是一个包含all()
,filter()
,get()
等的经理。创建自己的经理可以预先过滤结果并拥有所有这些相同的方法,以及您自己的自定义方法,研究结果。
编辑:我修改了我的代码以反映order_by['?']
方法。请注意,经理返回无限数量的随机模型。因此,我已经包含了一些使用代码来展示如何只获得一个模型。
from django.db import models
class RandomManager(models.Manager):
def get_query_set(self):
return super(RandomManager, self).get_query_set().order_by('?')
class Painting(models.Model):
title = models.CharField(max_length=100)
author = models.CharField(max_length=50)
objects = models.Manager() # The default manager.
randoms = RandomManager() # The random-specific manager.
用法
random_painting = Painting.randoms.all()[0]
最后,您可以在模型上拥有许多经理,因此可以随意创建LeastViewsManager()
或MostPopularManager()
。
这是一个简单的解决方案:
from random import randint
count = Model.objects.count()
random_object = Model.objects.all()[randint(0, count - 1)] #single random object
其他答案要么可能很慢(使用order_by('?')
),要么使用多个SQL查询。这是一个没有排序和一个查询的示例解决方案(假设Postgres):
Model.objects.raw('''
select * from {0} limit 1
offset floor(random() * (select count(*) from {0}))
'''.format(Model._meta.db_table))[0]
请注意,如果表为空,这将引发索引错误。给自己写一个与模型无关的辅助函数来检查它。
我只是一个简单的想法:
def _get_random_service(self, professional):
services = Service.objects.filter(professional=professional)
i = randint(0, services.count()-1)
return services[i]
我创建了一个模型管理器
models.py(示例)
from django.db import models
class RandomManager(models.Manager):
def get_random(self, items=1):
'''
items is integer value
By default it returns 1 random item
'''
if isinstance(items, int):
return self.model.objects.order_by('?')[:items]
return self.all()
class Category(models.Model):
name = models.CharField(max_length=100)
objects = RandomManager()
class Meta:
default_related_name = 'categories'
verbose_name = 'category'
verbose_name_plural = 'categories'
例如,您可以从数据库中获取随机项
Category.objects.get_random(5) # To get 5 random items
只是要注意一个(相当常见的)特殊情况,如果表中没有删除的索引自动增量列,则进行随机选择的最佳方法是查询,如:
SELECT * FROM table WHERE id = RAND() LIMIT 1
假设这样一个列为table的id。在django你可以通过以下方式完成:
Painting.objects.raw('SELECT * FROM appname_painting WHERE id = RAND() LIMIT 1')
您必须用您的应用程序名称替换appname。
一般情况下,使用id列,order_by('?')可以更快地完成:
Paiting.objects.raw(
'SELECT * FROM auth_user WHERE id>=RAND() * (SELECT MAX(id) FROM auth_user) LIMIT %d'
% needed_count)