如何使用Django的ORM提取随机记录?

ken*_*der 169 python django django-models

我有一个模特代表我在网站上出现的画作.在主要网页上,我想展示一些:最新的,大多数时间没有访问过的,最受欢迎的一个和随机的一个.

我正在使用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)
Run Code Online (Sandbox Code Playgroud)

在我看来,它看起来并不像我想要的东西 - 这完全是数据库抽象的一部分,应该在模型中.此外,在这里我需要处理删除的记录(然后所有记录的数量不会覆盖我所有可能的键值),可能还有很多其他的东西.

我可以做任何其他选择,最好以某种方式在模型抽象中?

muh*_*huk 245

只需使用:

MyModel.objects.order_by('?').first()
Run Code Online (Sandbox Code Playgroud)

它在QuerySet API中有记录.

  • 请注意,这种方法可能非常慢,如文档:) (66认同)
  • 为什么不只是`random.choice(Model.objects.all())`? (33认同)
  • 我读到它在mysql中很慢,因为mysql具有令人难以置信的低效随机排序. (8认同)
  • "可能很昂贵而且速度很慢,具体取决于您使用的数据库后端." - 有关不同数据库后端的任何经验?(源码/ MySQL的/ postgres的)? (6认同)
  • 我还没有测试过,所以这是纯粹的推测:为什么它应该比检索所有项目并在Python中执行随机化要慢? (4认同)
  • @Jamey当数据库堆中的记录数量很少且很热时,这是首选. (2认同)
  • 以下是您不应该使用随机排序的原因http://stackoverflow.com/questions/1731346/how-to-get-two-random-records-with-django/6405601#6405601 (2认同)
  • @kender - 使用Postgres表350,000行:成本~25,000,响应时间= 67ms.较小的数据集的性能也不错.一旦你获得了数百万行,那么下面的Emil解决方案要好得多. (2认同)

Emi*_*nov 158

使用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]
Run Code Online (Sandbox Code Playgroud)

  • `model.objects.aggregate(count = Count('id'))['count']`比`model.objects.all().count()有什么好处? (42认同)
  • 虽然比接受的答案要好得多,但请注意,此方法会产生两个SQL查询.如果计数之间发生变化,则可能会出现越界错误. (9认同)
  • 如果您的ID不连续,这将不起作用 (6认同)
  • 这是一个错误的解决方案。如果您的ID并非从0开始,那么它将不起作用。当ID不连续时,也无法使用。假设第一个记录从500开始,最后一个记录是599(假设连续)。然后,计数将为54950。由于您的查询长度为100,肯定不会存在list [54950]。它将使索引超出绑定异常。我不知道为什么有这么多人反对这个,这被标记为可接受的答案。 (2认同)
  • @NathanTuggy.好吧,我的坏.抱歉 (2认同)

Mik*_*bov 26

如果使用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]
Run Code Online (Sandbox Code Playgroud)

几乎在所有情况下,它都比order_by('?')快.

  • 还有,遗憾的是,它远非随意.如果你有一个id为1的记录而另一个id为100的记录,那么它将在99%的时间内返回第二个记录. (30认同)

小智 13

这是一个简单的解决方案:

from random import randint

count = Model.objects.count()
random_object = Model.objects.all()[randint(0, count - 1)] #single random object
Run Code Online (Sandbox Code Playgroud)


Sov*_*iut 10

您可以在模型上创建一个管理器来执行此类操作.首先要明白一个经理是什么,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.
Run Code Online (Sandbox Code Playgroud)

用法

random_painting = Painting.randoms.all()[0]
Run Code Online (Sandbox Code Playgroud)

最后,您可以在模型上拥有许多经理,因此可以随意创建LeastViewsManager()MostPopularManager().

  • 使用get()只有在你的pks是连续的时候才有效,即你永远不会删除任何项目.否则你可能会尝试获得一个不存在的PK.使用.all()[random_index]不会遇到这个问题,并且效率不高. (3认同)
  • 我不会创建一个新的经理只是为了容纳一个方法.我将"get_random"添加到默认管理器中,这样您每次需要随机图像时都不必通过all()[0]箍.此外,如果作者是User模型的ForeignKey,您可以说user.painting_set.get_random(). (2认同)

Nel*_*nim 5

其他答案可能很慢(使用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]
Run Code Online (Sandbox Code Playgroud)

请注意,如果表为空,这将引发索引错误。为自己编写一个与模型无关的辅助函数以进行检查。