Django:从查询集中获取10个随机实例,然后将其排序到新的查询集中?

Eaz*_*zyC 3 python django

我想为我的应用程序创建一个动态主页,每次访问该主页时,都会使用该网站的10个不同页面/个人资料。我知道随机查询的django SQL查询非常慢,所以我试图通过创建一个空列表,创建一个随机数列表,然后抓取第n个随机元素来编写自己的方法(伪)随机样本查询集并将其放入列表中。

import random
profilelist = [] #create an empty list
qindex = ProfilePage.objects.filter(profileisbannedis=False) #queryset for all of possible profiles to be displayed
randlist = random.sample(xrange(qindex.count()), 10) #create a list of 10 numbers between range 0 and the size of the queryset. 
#this method also does not repeat the same randomly generated number which is ideal since I don't want to feature the same profile twice
for i in randlist: 
    tile = qindex[i] #for each random number created, get that element of the queryset
    profilelist.extend(tile) #place each object in the previous queryset into a new list of objects and continue extending the list for each of 10 random numbers
Run Code Online (Sandbox Code Playgroud)

我真的不知道该怎么做,因为我知道在代码的最后一行收到错误“对象不可迭代”,因此像这样逐段创建一个新的查询集不是正确的方法。我该如何做/从以前的过滤查询集创建随机查询集?

Sau*_*yal 12

您可以做的一件事是获取查询集中随机元素的 id 列表(假设“id”是主键),然后对这些元素进行过滤。类似于下面的代码:

import random
valid_profiles_id_list = ProfilePage.objects.filter(profileisbannedis=False).values_list('id', flat=True)
random_profiles_id_list = random.sample(valid_profiles_id_list, min(len(valid_profiles_id_list), 10))
query_set = ProfilePage.objects.filter(id__in=random_profiles_id_list)
Run Code Online (Sandbox Code Playgroud)

希望它有帮助,也请通过django queryset docs

  • 对于 Python 3.8,您应该使用 list(valid_profiles_id_list) 作为 render.sample 函数的第一个参数。 (4认同)

Aus*_*n A 5

经过快速测试,我发现使用xrangewith random.sample确实提供了一个列表,因此xrange并不是您的问题。

>>> import random
>>> a = xrange(100)
>>> rnd = random.sample(a, 10)
>>> rnd
[41, 83, 89, 73, 37, 58, 38, 99, 10, 84]
Run Code Online (Sandbox Code Playgroud)

我以前用django做过。以下是该应用程序的代码段。我唯一不同的是count()在所有对象上使用而不是过滤器。我的下一个建议是确保Django过滤器上的计数符合您的期望。

# Choose 10 random records to show
num_entities = Entity.objects.all().count()
rand_entities = random.sample(range(num_entities), 10)
sample_entities = Entity.objects.filter(eid__in=rand_entities)
Run Code Online (Sandbox Code Playgroud)

  • 实际上,即使表仅是CREATE,READ和UPDATE,如果使用事务,它也将不起作用,因为回滚的事务不会恢复大多数数据库引擎(例如Postgres和Oracle)上的顺序。因此,即使没有删除任何行,您也会有空白。 (2认同)

gze*_*one 5

首先,我认为您应该将您的配置文件列表的“extend”替换为“append”,qindex[i] 不可迭代。

其次,我觉得最简单的方法是:

q_ids = qindex.values_list('id', flat=True)
r_ids = random.sample(q_ids, 10)
return qindex.filter(id__in=r_ids)
Run Code Online (Sandbox Code Playgroud)

试试, :)


Don*_*kby 5

在你变得比必要的复杂之前,我建议你测试一下order_by('?')你的数据库是否真的很慢。

在你的问题中,你说:

我知道随机查询的 django SQL 查询非常慢,所以我正在尝试编写自己的方法来执行此操作...

这是 Django文档所说的:

注意:order_by('?')查询可能既昂贵又缓慢,具体取决于您使用的数据库后端。

因此,您应该在这里检查您的数据库是否存在性能问题。

其他答案提出了两种选择:

  1. 选择 0 和 之间的随机数count-1,然后根据这些 id 进行过滤。
  2. 选择数据库中所有 id 的列表,然后从列表中随机选择一些,并根据这些 id 进行过滤。

选项 1 的边界条件和 ID 编号存在问题。选项 2 进行两次数据库查询,其中一次返回数据库中的所有 id 号。这两个选项都可以返回按 ID 编号排序的最终选择。

考虑到所有这些问题和增加的复杂性,您至少应该衡量收益以决定它是否值得。

下面是当我order_by('?')在 SQLite3 数据库中选择 1000 条小记录中的 10 条来测量选项 1 的性能时发生的情况:

Select in database with random order:
205, 49, 28, 542, 428, 1, 337, 860, 374, 303
[8.38821005821228, 7.809916019439697, 7.193678855895996, 8.39355993270874, 8.132720947265625]
Filter by random id numbers:
135, 357, 406, 476, 552, 580, 662, 663, 670, 889
[8.62951397895813, 8.145615100860596, 8.251683950424194, 7.629027843475342, 7.384187936782837]
Run Code Online (Sandbox Code Playgroud)

这是我在 PostgreSQL 中尝试相同操作时的结果:

Select in database with random order:
117, 337, 160, 500, 468, 178, 845, 542, 735, 525
[13.016371965408325, 12.65379810333252, 12.106752872467041, 12.485779047012329, 12.837188959121704]
Filter by random id numbers:
59, 65, 108, 161, 213, 246, 301, 813, 854, 969
[18.311591863632202, 20.5823872089386, 13.955725193023682, 13.034253120422363, 13.079485177993774]
Run Code Online (Sandbox Code Playgroud)

如果有显着差异,则order_by('?')看起来更好。它在您的数据库中看起来如何?如果您决定使用选项 1,请更改边界以匹配您的 ID 号。如果您决定选择选项 2,其他答案看起来不错。

这是我用来测试 SQLite3 版本的代码。您可以将其保存到文件中并按原样运行:

# Tested with Django 1.9.2
import sys
import timeit
from random import sample

import django
from django.apps import apps
from django.apps.config import AppConfig
from django.conf import settings
from django.db import connections, models, DEFAULT_DB_ALIAS
from django.db.models.base import ModelBase

NAME = 'udjango'
SELECT_COUNT = 10
base_query = None


def select_in_database():
    chosen = base_query.order_by('?')[:SELECT_COUNT]
    return list(chosen)


def select_by_random_id():
    db_size = base_query.count()
    random_ids = sample(xrange(db_size), SELECT_COUNT)
    chosen = base_query.filter(id__in=random_ids)
    return list(chosen)


def main():
    global base_query
    setup()

    class Person(models.Model):
        first_name = models.CharField(max_length=30)
        last_name = models.CharField(max_length=30)

    syncdb(Person)

    for i in range(1000):
        Person.objects.create(first_name=str(i), last_name=str(i))

    base_query = Person.objects.all()

    print('Select in database with random order:')
    print(', '.join(person.first_name for person in select_in_database()))
    print(timeit.repeat('select_in_database()',
                        'from __main__ import select_in_database',
                        repeat=5,
                        number=10000))
    print('Filter by random id numbers:')
    print(', '.join(person.first_name for person in select_by_random_id()))
    print(timeit.repeat('select_by_random_id()',
                        'from __main__ import select_by_random_id',
                        repeat=5,
                        number=10000))


def setup():
    DB_FILE = NAME + '.db'
    with open(DB_FILE, 'w'):
        pass  # wipe the database
    settings.configure(
        DEBUG=True,
        DATABASES={
            DEFAULT_DB_ALIAS: {
                'ENGINE': 'django.db.backends.sqlite3',
                'NAME': DB_FILE}},
        LOGGING={'version': 1,
                 'disable_existing_loggers': False,
                 'formatters': {
                    'debug': {
                        'format': '%(asctime)s[%(levelname)s]'
                                  '%(name)s.%(funcName)s(): %(message)s',
                        'datefmt': '%Y-%m-%d %H:%M:%S'}},
                 'handlers': {
                    'console': {
                        'level': 'DEBUG',
                        'class': 'logging.StreamHandler',
                        'formatter': 'debug'}},
                 'root': {
                    'handlers': ['console'],
                    'level': 'WARN'},
                 'loggers': {
                    "django.db": {"level": "WARN"}}})
    app_config = AppConfig(NAME, sys.modules['__main__'])
    apps.populate([app_config])
    django.setup()
    original_new_func = ModelBase.__new__

    @staticmethod
    def patched_new(cls, name, bases, attrs):
        if 'Meta' not in attrs:
            class Meta:
                app_label = NAME
            attrs['Meta'] = Meta
        return original_new_func(cls, name, bases, attrs)
    ModelBase.__new__ = patched_new


def syncdb(model):
    """ Standard syncdb expects models to be in reliable locations.

    Based on https://github.com/django/django/blob/1.9.3
    /django/core/management/commands/migrate.py#L285
    """
    connection = connections[DEFAULT_DB_ALIAS]
    with connection.schema_editor() as editor:
        editor.create_model(model)


main()
Run Code Online (Sandbox Code Playgroud)