Каков порядок по умолчанию списка, возвращаемого вызовом фильтра Django?

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

Предыстория
По моему собственному признанию, я сделал неверное предположение на прикладном уровне о том, что порядок, в котором возвращается список, будет постоянным, т.е. используя 'order_by'. Список элементов, которые я запрашивал, не в алфавитном порядке или в каком-либо другом преднамеренном порядке. Считалось, что они останутся в том же порядке, в котором они были добавлены в базу данных.

Это предположение было верным для сотен запросов, но мое приложение сообщило об ошибке, когда порядок непреднамеренно изменился. Насколько мне известно, ни одна из этих записей не была затронута за это время, поскольку я единственный человек, который поддерживает БД. Чтобы добавить путаницы, при запуске приложения Django в Mac OS X оно по-прежнему работало, как и ожидалось, но в Win XP оно изменило порядок. (Обратите внимание, что упомянутые сотни запросов были на Win XP).

Любое понимание этого было бы полезно, поскольку я не смог найти ничего в документации Django или PostgreSQL, объясняющей различия в операционных системах.

Пример звонка

required_tests = Card_Test.objects.using(get_database()).filter(name__icontains=key)

РЕДАКТИРОВАТЬ
Поговорив сегодня с одним из моих коллег, я пришел к тому же ответу, что и Бьорн Линдквист.

Оглядываясь назад, я определенно понимаю, почему это так часто делается неправильно. Одним из преимуществ использования ORM Django, sqlalchemy или чего-либо еще является то, что вы можете писать команды без необходимости знать или понимать (подробно) базу данных, к которой она подключена. Признаюсь, я был одним из таких пользователей. Однако оборотной стороной этого является то, что без детального знания базы данных ошибки отладки, подобные этой, довольно неприятны и потенциально катастрофичны.


person Adam Lewis    schedule 23.08.2011    source источник


Ответы (2)


Существует НЕТ ПОРЯДКА ПО УМОЛЧАНИЮ, момент, который невозможно подчеркнуть достаточно, потому что все делают это неправильно.

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

select * from table limit 10
select * from table limit 10 offset 10
select * from table order by x limit 10

В последнем запросе порядок предсказуем, только если все значения в столбце x уникальны. СУРБД может возвращать любые строки в любом порядке, если он удовлетворяет условиям оператора select.

Хотя вы можете добавить порядок по умолчанию на уровне Django, что приведет к добавлению предложения order by к каждому неупорядоченному запросу:

class Table(models.Model):
    ...
    class Meta:
        ordering = ['name']

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

person Björn Lindqvist    schedule 23.08.2011
comment
Django docs: если запрос не имеет указанный порядок, результаты возвращаются из базы данных в неопределенном порядке. Конкретный порядок гарантируется только при упорядочении по набору полей, которые однозначно идентифицируют каждый объект в результатах. Например, если поле имени не уникально, упорядочение по нему не гарантирует, что объекты с одинаковым именем всегда будут отображаться в одном и том же порядке. - person Paolo; 15.07.2018
comment
Если я хорошо это понимаю, я всегда должен добавлять class Meta: ordering, и если перечисленные поля не гарантированно уникальны, я всегда должен добавлять `[...., 'id'] в качестве последнего элемента. - person mirek; 04.03.2021

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

Добавьте в модель следующее:

created = models.DateTimeField(auto_now_add=True, db_index=True)
# last_modified = models.DateTimeField(auto_now=True, db_index=True)

class Meta:
    ordering = ['created',]
    # ordering = ['-last_modified']  # sort last modified first
person Risadinha    schedule 18.02.2016
comment
Вместо того, чтобы изменять модель для добавления нового поля, почему бы просто не упорядочить по автоматически увеличивающемуся идентификатору, который есть у большинства моделей? Он не подвержен проблемам с точностью (вы должны беспокоиться о степени детализации DateTimeField) или обмануты изменениями системного времени. - person aggieNick02; 05.10.2017
comment
Это работает, только если вы действительно уверены, что вставки всегда используют auto-incr. последовательности и никогда не используют повторно неиспользуемые идентификаторы, дампы из других БД или таблиц, которые устанавливают время создания, которое не будет отражено в порядке первичного ключа. Непрозрачно использовать идентификатор, когда вы действительно хотите отсортировать по дате — и часто вы действительно хотите отсортировать по last_modified — и идентификатор в этом случае не поможет. - person Risadinha; 05.10.2017
comment
Я согласен, что есть некоторые крайние случаи, когда вы можете захотеть сделать что-то нестандартное, но по умолчанию каждая модель получает автоматически увеличивающийся идентификатор, и повторное использование неиспользуемого идентификатора невозможно. Кроме того, с DateTimeField у вас возникает проблема, если две модели получают одно и то же значение в своем DateTimeField. Это может произойти в зависимости от точности из-за версии/базы данных django. - person aggieNick02; 05.10.2017