如何使用Django的ORM拉随机logging?

我有一个模型,代表我在网站上展示的绘画。 在主网页上,我想展示其中的一些:最新的,大多数时间没有访问的,最stream行的和随机的。

我正在使用Django 1.0.2。

虽然前3个很容易使用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) 

这看起来不像我想要的东西 – 这完全是数据库抽象的一部分,应该在模型中。 此外,在这里我需要照顾删除logging(然后所有logging的数量将不会涵盖所有可能的关键值),可能还有很多其他的东西。

任何其他选项我怎么能做到这一点,最好是在模型抽象内部?

使用order_by('?')会在生产的第二天杀死数据库服务器。 更好的方法就像在从关系数据库中获取随机行中描述的那样。

 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] 

只需使用:

 MyModel.objects.order_by('?').first() 

它在QuerySet API中有logging 。

如果使用MySQL(不知道其他数据库),使用order_by('?')[:N]的解决scheme即使对于中等大小的表也非常慢。

order_by('?')[:N]将被转换为SELECT ... FROM ... WHERE ... ORDER BY RAND() LIMIT N查询。

这意味着对于表中的每一行都会执行RAND()函数,然后整个表将按照该函数的值进行sorting,然后返回前N个logging。 如果你的桌子很小,这很好。 但在大多数情况下,这是一个非常缓慢的查询。

我写了一个简单的函数,即使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('?')更快。

你可以在你的模型上创build一个pipe理器来做这种事情。 为了首先了解pipe理者是什么, Painting.objects方法是一个包含all()filter()get()等的pipe理器。创build自己的pipe理器允许您预先筛选结果并使用所有这些方法,以及你自己的习惯方法,在结果上工作。

编辑 :我修改我的代码,以反映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] 

最后,你可以在你的模型上有许多经理,所以随意创build一个LeastViewsManager()或者MostPopularManager()

其他答案可能很慢(使用order_by('?') )或使用多个SQL查询。 这里有一个没有sorting和只有一个查询(假设Postgres)的示例解决scheme:

 Model.objects.raw(''' select * from {0} limit 1 offset floor(random() * (select count(*) from {0})) '''.format(Model._meta.db_table))[0] 

请注意,如果表格为空,则会引发索引错误。 写自己的模型不可知的帮助函数来检查。

这是高度推荐从关系数据库中获取一个随机行

因为使用django orm来做这样的事情,如果你有大数据表会使你的db服务器生气:

解决scheme是提供一个模型pipe理器,并手工编写SQL查询;)

更新

另一种解决scheme,可以在任何数据库后端工作,甚至不需要编写自定义模型pipe理器的非rel相关。 在Django中从Queryset中获取Random对象

只是一个简单的想法,我怎么做:

 def _get_random_service(self, professional): services = Service.objects.filter(professional=professional) i = randint(0, services.count()-1) return services[i] 

一个更简单的方法就是简单地过滤到感兴趣的logging集,然后使用random.sample来select尽可能多的数据:

 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 

请注意,您应该有一些代码来validationmy_queryset不是空的; random.sample返回ValueError: sample larger than population如果第一个参数包含的元素太less,则ValueError: sample larger than population

这是一个简单的解决scheme:

 from random import randint count = Model.objects.count() random_object = Model.objects.all()[randint(0, count - 1)] #single random object 

只需要注意一个(相当常见的)特殊情况,如果表中没有删除索引的自动增量列,那么执行随机select的最佳方法是查询如下:

 SELECT * FROM table WHERE id = RAND() LIMIT 1 

假定这样一个名为id的列用于表格。 在Django中,您可以通过以下方式来完成此操作:

 Painting.objects.raw('SELECT * FROM appname_painting WHERE id = RAND() LIMIT 1') 

在其中您必须用应用程序名称replaceappname。

一般来说,使用id列,order_by('?')可以更快地完成:

 Paiting.objects.raw( 'SELECT * FROM auth_user WHERE id>=RAND() * (SELECT MAX(id) FROM auth_user) LIMIT %d' % needed_count) 

您可能希望使用与用于对任何迭代器进行采样的相同方法 ,特别是如果您计划对多个项目进行采样以创build一个采样集合 。 @MatijnPieters和@DzinX把这个很多的想法:

 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 

简单地这样做有什么不妥:

 import random records = Model.objects.all() random_record = random.choice(records)