Как получить две случайные записи с Django

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

Ответ 1

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

MyModel.objects.order_by('?')[:2] # 2 random results.

Ответ 2

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

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

Примечание: просто для небольшого дополнительного внимания, имейте в виду, что первоначально 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]