Решение order_by('?')[:2]
, предлагаемое другими ответами, на самом деле является чрезвычайно плохой вещью для таблиц с большим количеством строк. Это приводит к ORDER BY RAND()
запросу SQL. В качестве примера, вот как mysql справляется с этим (ситуация не сильно отличается для других баз данных). Представьте, что ваша таблица содержит один миллиард строк:
- Чтобы выполнить
ORDER BY RAND()
, для сортировки требуется столбец RAND()
.
- Для этого ему нужна новая таблица (в существующей таблице такого столбца нет).
- Для этого mysql создает новую временную таблицу с новыми столбцами и копирует в нее ОДИН МИЛЛИАРД СТРОК ДАННЫХ.
- При этом он делает, как вы просили, и запускает rand () для каждой строки, чтобы заполнить это значение. Да, вы поручили mysql СОЗДАТЬ ОДИН МИЛЛИАРД СЛУЧАЙНЫХ ЧИСЕЛ. Это занимает некоторое время. :)
- Несколько часов / дней спустя, когда это будет сделано, теперь нужно его отсортировать. Да, вы дали команду mysql СОРТИРОВАТЬ ЭТОТ ОДИН МИЛЛИАРДНЫЙ СТОЛ, ТАБЛИЦА С УСТРОЙСТВОМ НАИМЕНЬШЕГО СЛУЧАЯ (наихудший случай, потому что ключ сортировки случайный).
- Через несколько дней / недель, когда это будет сделано, он добросовестно захватывает два нужных вам ряда и возвращает их вам. Хорошая работа. ;)
Примечание: только для небольшого дополнительного внимания, имейте в виду, что 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]