Как получить случайную запись, используя ORM Джанго? - PullRequest
164 голосов
/ 07 июня 2009

У меня есть модель, которая представляет картины, которые я представляю на своем сайте. На главной веб-странице я хотел бы показать некоторые из них: новейшую, ту, которую не посещали большую часть времени, самую популярную и случайную.

Я использую Django 1.0.2.

В то время как первые 3 из них легко получить с помощью моделей django, последняя (случайная) доставляет мне некоторые проблемы. Я могу сделать это, на мой взгляд, примерно так:

number_of_records = models.Painting.objects.count()
random_index = int(random.random()*number_of_records)+1
random_paint = models.Painting.get(pk = random_index)

Это не похоже на то, что я хотел бы иметь, на мой взгляд, - это полностью часть абстракции базы данных и должно быть в модели. Кроме того, здесь мне нужно позаботиться об удаленных записях (тогда число всех записей не покроет меня всеми возможными значениями ключей) и, возможно, множеством других вещей.

Любые другие варианты, как я могу это сделать, желательно как-то внутри абстракции модели?

Ответы [ 15 ]

1 голос
/ 30 октября 2016

Один гораздо более простой подход к этому заключается в простой фильтрации интересующего набора записей и использовании random.sample, чтобы выбрать столько, сколько вы хотите:

from myapp.models import MyModel
import random

my_queryset = MyModel.objects.filter(criteria=True)  # Returns a QuerySet
my_object = random.sample(my_queryset, 1)  # get a single random element from my_queryset
my_objects = random.sample(my_queryset, 5)  # get five random elements from my_queryset

Обратите внимание, что у вас должен быть какой-то код для проверки того, что my_queryset не пусто; random.sample возвращает ValueError: sample larger than population, если первый аргумент содержит слишком мало элементов.

1 голос
/ 05 декабря 2015

Возможно, вы захотите использовать такой же подход , который вы использовали бы для выборки любого итератора, особенно если вы планируете выбрать несколько элементов для создания выборочного набора . @MatijnPieters и @DzinX много думают об этом:

def random_sampling(qs, N=1):
    """Sample any iterable (like a Django QuerySet) to retrieve N random elements

    Arguments:
      qs (iterable): Any iterable (like a Django QuerySet)
      N (int): Number of samples to retrieve at random from the iterable

    References:
      @DZinX:  https://stackoverflow.com/a/12583436/623735
      @MartinPieters: https://stackoverflow.com/a/12581484/623735
    """
    samples = []
    iterator = iter(qs)
    # Get the first `N` elements and put them in your results list to preallocate memory
    try:
        for _ in xrange(N):
            samples.append(iterator.next())
    except StopIteration:
        raise ValueError("N, the number of reuested samples, is larger than the length of the iterable.")
    random.shuffle(samples)  # Randomize your list of N objects
    # Now replace each element by a truly random sample
    for i, v in enumerate(qs, N):
        r = random.randint(0, i)
        if r < N:
            samples[r] = v  # at a decreasing rate, replace random items
    return samples
1 голос
/ 31 мая 2012

Просто чтобы отметить (довольно распространенный) особый случай, если в таблице есть индексированный столбец автоинкремента без удалений, оптимальный способ сделать случайный выбор - это запрос, подобный:

SELECT * FROM table WHERE id = RAND() LIMIT 1

, который предполагает такой столбец с именем id для таблицы. В Django вы можете сделать это:

Painting.objects.raw('SELECT * FROM appname_painting WHERE id = RAND() LIMIT 1')

, в котором вы должны заменить имя приложения на имя вашего приложения.

В общем, с помощью столбца id, order_by ('?') Может быть выполнен намного быстрее с помощью:

Paiting.objects.raw(
        'SELECT * FROM auth_user WHERE id>=RAND() * (SELECT MAX(id) FROM auth_user) LIMIT %d' 
    % needed_count)
1 голос
/ 10 мая 2012

Рекомендуется Получение случайной строки из реляционной базы данных

Поскольку использование django orm для подобных вещей, особенно раздражает ваш db-сервер, если у вас большая таблица данных: |

И решение состоит в том, чтобы предоставить Менеджер моделей и написать SQL-запрос вручную;)

Обновление

Еще одно решение, которое работает с любой базой данных, даже не относящейся к базе данных, без написания пользовательских ModelManager. Получение случайных объектов из Queryset в Django

0 голосов
/ 13 мая 2019

Метод автоинкрементного первичного ключа без удалений

Если у вас есть таблица, в которой первичный ключ представляет собой последовательное целое число без пробелов, то должен работать следующий метод:

import random
max_id = MyModel.objects.last().id
random_id = random.randint(0, max_id)
random_obj = MyModel.objects.get(pk=random_id)

Этот метод гораздо более эффективен, чем другие методы, которые здесь повторяют все строки таблицы. Хотя это требует двух запросов к базе данных, оба тривиальны. Кроме того, это просто и не требует определения дополнительных классов. Однако его применимость ограничена таблицами с автоинкрементным первичным ключом, где строки никогда не удаляются, так что в последовательности идентификаторов нет пробелов.

В случае, когда строки были удалены таким образом, что являются пробелами, этот метод все еще может работать, если он повторяется до случайного выбора существующего первичного ключа.

Ссылки

...