Django, как получить 0-й элемент из возможно пустого списка

У меня есть простое приложение для блога с моделью «Пост». Если я удалю все записи в модели Post, я получаю сообщение об ошибке, когда пытаюсь сослаться на первый элемент в списке сообщений, упорядоченном по дате, что я и сделал:

latest_post = Post.objects.order_by('-date_created')[0]

Ошибка: IndexError: индекс списка вне допустимого диапазона

В качестве исправления я теперь получаю такой элемент:

all_posts = Post.objects.order_by('-date_created')
latest_post = ()
if (all_posts):
  latest_post = all_posts[0]

Это работает, если в моей модели "Post" нет элементов и не выдается исключение. Однако мне кажется, что это слишком много кода, чтобы сделать что-то довольно простое. Я предполагаю, что есть лучший способ сделать это с помощью django QuerySet API, но ничего не могу найти в документации.

Любые идеи?

РЕДАКТИРОВАТЬ: Как ни странно, это не вызывает ошибки, когда в модели Post нет элементов:

latest_post_list = Post.objects.all().order_by('-date_created')[1:10]

person BoomShaka    schedule 05.07.2011    source источник


Ответы (3)


Ничего странного в этом нет, это вполне ожидаемое поведение. Пустой список (или набор запросов, в частности, в данном случае) оценивается как False, поэтому вы никогда не индексируете набор запросов. В то время как, если вы попытаетесь проиндексировать пустой список (как вы делаете с первым подходом), он выдаст ошибку IndexError.

То, что вы написали, будет работать, но это не лучший вариант. Лучший способ написать это было бы так

try:
    latest_post = Post.objects.order_by('-date_created')[0]
except IndexError:
    latest_post = None

Это более питонический способ написания, его легче читать и понимать, что вы пытаетесь сделать.

Или даже лучше

try:
    latest_post = Post.objects.latest('date_created')
except Post.DoesNotExist:
    latest_post = None

Обратите внимание, что во втором примере используется метод набора запросов last(). Также обратите внимание, что аргументом является просто имя поля, а не -fieldname. Также вы можете даже указать в своих моделях мета-класс get_latest_by = 'date_created', и тогда строка просто станет latest_post = Post.objects.latest(), даже не указывая аргумент имени поля.

person John    schedule 05.07.2011
comment
+1 за try... except. Легче попросить прощения, чем разрешения. - person Shawn Chin; 05.07.2011
comment
+1 за предложение последней версии как еще лучший способ решить проблему. - person rolling stone; 05.07.2011
comment
Обратите внимание, что .latest() не отфильтровывает нулевые записи - и обычно сначала сортируются нулевые значения :). Итак, если даты могут быть нулевыми, вам нужно Post.objects.exclude(date_created=None).latest('date_created') - person rcoup; 08.04.2014

Простое исправление python состояло бы в том, чтобы вместо этого использовать

latest_post = Post.objects.order_by('-date_created')[0:1]
person stefan    schedule 05.07.2011

latest_post = Post.objects.order_by('-date_created')[:1]
if latest_post:
    latest_post = latest_post[0]

Это не приведет к возникновению исключения, и вы все равно можете проверить состояние пустого списка в last_post.

Ваша проблема возникает из-за использования абсолютного индекса IE: [0], который может существовать или не существовать. Используя срез [:1], вы говорите, что вам нужен список с первым элементом в списке, если он существует... если он не существует, вы просто получите пустой список.

person Howard    schedule 05.07.2011
comment
Извините, аналогичный ответ дал Стефан - person Howard; 05.07.2011
comment
исправление: при использовании [:1], если нет результатов, вы получаете пустой список ([]), а не None. - person Shawn Chin; 05.07.2011
comment
Исправлен возврат, чтобы отразить результат пустого списка. - person Howard; 06.07.2011
comment
list[:1] возвращает список размера один или список размера ноль. Так что вам все равно придется проверить размер списка позже. Я редактирую ответ, чтобы отразить это. - person Flimm; 28.12.2015