Как получить две разные случайные записи с помощью Django? Я видел вопросы о том, как их получить, но мне нужно получить две случайные записи, и они должны отличаться.
Как получить две случайные записи с Django
Ответ 1
Если вы укажете случайный оператор в ORM, я уверен, что он даст вам два отдельных случайных результата, не так ли?
MyModel.objects.order_by('?')[:2] # 2 random results.
Ответ 2
Решение order_by('?')[:2]
предложенное другими ответами, на самом деле чрезвычайно плохо для таблиц с большим количеством строк. Это приводит к SQL-запросу ORDER BY RAND()
. В качестве примера, вот как MySQL обрабатывает это (ситуация не сильно отличается для других баз данных). Представьте, что ваша таблица содержит один миллиард строк:
- Чтобы выполнить
ORDER BY RAND()
, ему нужен столбецRAND()
для сортировки. - Для этого ему нужна новая таблица (в существующей таблице такой столбец отсутствует).
- Для этого mysql создает новую временную таблицу с новыми столбцами и копирует в нее ОДИН МИЛЛИАРДНЫЙ СТРОК ДАННЫХ.
- При этом он делает, как вы просили, и запускает rand() для каждой строки, чтобы заполнить это значение. Да, вы дали команду mysql СОЗДАТЬ ОДИН МИЛЛИАРД СЛУЧАЙНЫХ ЧИСЕЛ. Это занимает некоторое время. :)
- Спустя несколько часов/дней, когда это сделано, теперь нужно его отсортировать. Да, вы дали команду mysql СОРТИРОВАТЬ ЭТОТ ОДИН МИЛЛИАРДНЫЙ СТОЛ, СТОРОННУЮ СТОРОНУ СТОЛ (худший случай, потому что ключ сортировки случайный).
- Через несколько дней/недель, когда это будет сделано, он добросовестно захватывает два нужных вам ряда и возвращает их вам. Хорошая работа. ;)
Примечание: просто для небольшого дополнительного внимания, имейте в виду, что первоначально mysql попытается создать эту временную таблицу в оперативной памяти. Когда это исчерпано, все приостанавливается, чтобы скопировать все это на диск, так что вы получаете дополнительное закручивание ножом узкого места ввода/вывода почти для всего процесса.
Сомневающиеся должны посмотреть на сгенерированный запрос, чтобы убедиться, что он ORDER BY RAND()
затем Google для "order by rand()" (с кавычками).
Гораздо лучшее решение - обменять один действительно дорогой запрос на три дешевых (ограничение/смещение вместо ORDER BY RAND()
):
import random
last = MyModel.objects.count() - 1
index1 = random.randint(0, last)
# Here 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]
Ответ 3
Для будущих читателей.
Получить список идентификаторов всех записей:
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)
Ответ 4
Object.objects.order_by('?')[:2]
Это приведет к возврату двух случайных записей. Вы можете добавить
distinct()
если в вашем наборе данных есть записи с одинаковым значением.
Ответ 5
О выборке n случайных значений из последовательности, можно использовать случайную lib,
random.Random().sample(range(0,last),2)
будет извлекать 2 случайных выборки из элементов последовательности, от 0 до последнего-1
Ответ 6
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]