Как получить две случайные записи с Джанго - PullRequest
28 голосов
/ 13 ноября 2009

Как получить две разные случайные записи, используя Django? Я видел вопросы о том, как получить один, но мне нужно получить две случайные записи, и они должны отличаться.

Ответы [ 6 ]

96 голосов
/ 20 июня 2011

Решение order_by('?')[:2], предлагаемое другими ответами, на самом деле является чрезвычайно плохой вещью для таблиц с большим количеством строк. Это приводит к ORDER BY RAND() запросу SQL. В качестве примера, вот как mysql справляется с этим (ситуация не сильно отличается для других баз данных). Представьте, что ваша таблица содержит один миллиард строк:

  1. Чтобы выполнить ORDER BY RAND(), для сортировки требуется столбец RAND().
  2. Для этого ему нужна новая таблица (в существующей таблице такого столбца нет).
  3. Для этого mysql создает новую временную таблицу с новыми столбцами и копирует в нее ОДИН МИЛЛИАРД СТРОК ДАННЫХ.
  4. При этом он делает, как вы просили, и запускает rand () для каждой строки, чтобы заполнить это значение. Да, вы поручили mysql СОЗДАТЬ ОДИН МИЛЛИАРД СЛУЧАЙНЫХ ЧИСЕЛ. Это занимает некоторое время. :)
  5. Несколько часов / дней спустя, когда это будет сделано, теперь нужно его отсортировать. Да, вы дали команду mysql СОРТИРОВАТЬ ЭТОТ ОДИН МИЛЛИАРДНЫЙ СТОЛ, ТАБЛИЦА С УСТРОЙСТВОМ НАИМЕНЬШЕГО СЛУЧАЯ (наихудший случай, потому что ключ сортировки случайный).
  6. Через несколько дней / недель, когда это будет сделано, он добросовестно захватывает два нужных вам ряда и возвращает их вам. Хорошая работа. ;)

Примечание: только для небольшого дополнительного внимания, имейте в виду, что mysql первоначально попытается создать эту временную таблицу в RAM. Когда он исчерпан, он приостанавливает все, чтобы скопировать все это на диск, так что вы получаете дополнительное закручивание ножом узкого места ввода / вывода почти для всего процесса.

Сомневающиеся должны посмотреть на сгенерированный запрос, чтобы подтвердить, что это 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]
23 голосов
/ 13 ноября 2009

Если вы зададите случайный оператор в ORM, я уверен, что он даст вам два разных случайных результата, не так ли?

MyModel.objects.order_by('?')[:2] # 2 random results.
8 голосов
/ 21 июля 2016

Для будущих читателей.

Получить список идентификаторов всех записей:

my_ids = MyModel.objects.values_list('id', flat=True)
my_ids = list(my_ids)

Затем выберите n случайных идентификаторов из всех приведенных выше идентификаторов:

n = 2
rand_ids = random.sample(my_ids, n)

И получите записи для этих идентификаторов:

random_records = MyModel.objects.filter(id__in=rand_ids)
6 голосов
/ 13 ноября 2009

Object.objects.order_by('?')[:2]

Это вернет две случайно упорядоченные записи. Вы можете добавить

distinct()

если в вашем наборе данных есть записи с одинаковым значением.

3 голосов
/ 07 декабря 2013

О выборке n случайных значений из последовательности, можно использовать случайную библиотеку,

random.Random().sample(range(0,last),2) 

выберет 2 случайные выборки из числа элементов последовательности, от 0 до последней-1

0 голосов
/ 13 октября 2016

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]

Вы можете получить различное количество объектов.

...