У меня есть модель, которая представляет картины, которые я представляю на своем сайте. На главной веб-странице я хотел бы показать некоторые из них: новейшую, ту, которую не посещали большую часть времени, самую популярную и случайную.
Я использую 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)
На мой взгляд, это не похоже на то, что я хотел бы иметь - это полностью часть абстракции базы данных и должно быть в модели. Кроме того, здесь я должен позаботиться об удаленных записях (тогда число всех записей не покроет мне все возможные значения ключей) и, возможно, о многих других вещах.
Любые другие варианты, как я могу это сделать, желательно как-то внутри абстракции модели?
источник
Ответы:
Использование
order_by('?')
убьет сервер БД на второй день работы. Лучшим способом является то, что описано в разделе Получение случайной строки из реляционной базы данных .источник
model.objects.aggregate(count=Count('id'))['count']
болееmodel.objects.all().count()
.all()[randint(0, count - 1)]
в действительности. Возможно, вам следует сосредоточиться на выявлении того, какая часть ответа является неправильной или слабой, а не на том, чтобы переопределить для нас «ошибочную ситуацию» и кричать на глупых избирателей. (Может быть, это то, что он не использует.objects
?)Просто используйте:
Это задокументировано в QuerySet API .
источник
random.choice(Model.objects.all())
?Решения с помощью order_by ('?') [: N] чрезвычайно медленны даже для таблиц среднего размера, если вы используете MySQL (не знаю о других базах данных).
order_by('?')[:N]
будет переведен наSELECT ... FROM ... WHERE ... ORDER BY RAND() LIMIT N
запрос.Это означает, что для каждой строки в таблице будет выполнена функция RAND (), затем вся таблица будет отсортирована по значению этой функции, а затем будут возвращены первые N записей. Если ваши столы маленькие, это нормально. Но в большинстве случаев это очень медленный запрос.
Я написал простую функцию, которая работает, даже если у id есть дыры (некоторые строки были удалены):
Это быстрее, чем order_by ('?') Почти во всех случаях.
источник
Вот простое решение:
источник
Вы можете создать менеджера в вашей модели, чтобы делать подобные вещи. Для того, чтобы сначала понять , что менеджер, то
Painting.objects
метод является менеджером , который содержитall()
,filter()
,get()
и т.д. Создание собственного менеджера позволяет предварительно фильтр результаты и имеют все те же самые методы, а также свои собственные методы, пользовательские работы по результатам ,РЕДАКТИРОВАТЬ : я изменил свой код, чтобы отразить
order_by['?']
метод. Обратите внимание, что менеджер возвращает неограниченное количество случайных моделей. Из-за этого я включил немного кода использования, чтобы показать, как получить только одну модель.использование
Наконец, у вас может быть много менеджеров на ваших моделях, так что не стесняйтесь создавать
LeastViewsManager()
илиMostPopularManager()
.источник
Другие ответы являются либо потенциально медленными (используются
order_by('?')
), либо используют более одного запроса SQL. Вот пример решения без упорядочения и только одного запроса (при условии Postgres):Имейте в виду, что это приведет к ошибке индекса, если таблица пуста. Напишите себе вспомогательную функцию, независимую от модели, чтобы проверить это.
источник
count()
заранее и отказаться от необработанного запроса.Просто простая идея, как я это делаю:
источник
Просто чтобы отметить (довольно распространенный) особый случай, если в таблице есть индексированный столбец автоинкремента без удалений, оптимальный способ сделать случайный выбор - это запрос, подобный следующему:
который предполагает такой столбец с именем id для таблицы. В Django вы можете сделать это:
в котором вы должны заменить имя приложения на имя вашего приложения.
В общем, с помощью столбца id, order_by ('?') Можно сделать намного быстрее с помощью:
источник
Настоятельно рекомендуется
получить случайную строку из реляционной базы данныхПотому что использование django orm для подобных вещей, особенно раздражает ваш db-сервер, если у вас большая таблица данных: |
И решение состоит в том, чтобы предоставить Менеджер моделей и написать SQL-запрос вручную;)
Обновить :
Другое решение, которое работает с любой базой данных, даже не относящейся к базе данных, без написания пользовательских
ModelManager
. Получение случайных объектов из Queryset в Djangoисточник
Возможно, вы захотите использовать тот же подход, который вы использовали бы для выборки любого итератора, особенно если вы планируете выбрать несколько элементов для создания выборочного набора . @MatijnPieters и @DzinX много думают об этом:
источник
OFFSET
), это излишне неэффективно.Один гораздо более простой подход к этому включает в себя простую фильтрацию до интересующего набора записей и использование
random.sample
для выбора столько, сколько вы хотите:Обратите внимание, что у вас должен быть какой-то код, чтобы убедиться, что
my_queryset
он не пустой;random.sample
возвращает,ValueError: sample larger than population
если первый аргумент содержит слишком мало элементов.источник
Queryset
(по крайней мере, с Python 3.7 и Django 2.1); сначала нужно преобразовать его в список, который, очевидно, извлекает весь набор запросов.Привет, мне нужно было выбрать случайную запись из набора запросов, длину которой я также должен был сообщить (т.е. веб-страница произвела описанный элемент и оставила указанные записи)
потребовалось вдвое меньше (0,7 с против 1,7 с), как:
Я предполагаю, что это избегает сносить весь запрос перед выбором случайной записи и делает мою систему достаточно отзывчивой для страницы, к которой обращаются неоднократно для повторяющейся задачи, где пользователи хотят видеть обратный отсчет item_count.
источник
Метод автоматического увеличения первичного ключа без удалений
Если у вас есть таблица, в которой первичный ключ представляет собой последовательное целое число без пробелов, тогда должен работать следующий метод:
Этот метод гораздо более эффективен, чем другие методы, которые выполняют итерацию по всем строкам таблицы. Хотя это требует двух запросов к базе данных, оба тривиальны. Кроме того, это просто и не требует определения дополнительных классов. Однако его применимость ограничена таблицами с автоинкрементным первичным ключом, где строки никогда не удаляются, так что в последовательности идентификаторов нет пробелов.
В случае, когда строки были удалены, такие как пробелы, этот метод все еще может работать, если он повторяется до случайного выбора существующего первичного ключа.
Ссылки
источник
Я получил очень простое решение, сделать собственный менеджер:
а затем добавить в модель:
Теперь вы можете использовать его:
источник
order_by('?').first()
более чем в 60 раз.