Django: повторно используйте код для перенаправления на отсутствующий объект

#python #django #django-views

#python #django #django-представления

Вопрос:

У меня есть сайт, URL-адреса которого выглядят как

http://www.example.com/NY-2010/

http://www.example.com/NY-2010/location/

http://www.example.com/NY-2010/something-else/

http://www.example.com/Washington-2009/

http://www.example.com/Washington-2009/location/

http://www.example.com/Washington-2009/something-else/

и так далее. Существуют различные страницы (например, location) для различных редакций (например, NY). Я использую URLconfs как

 url(r'^(?P<edition>[d] -[w] )/$', views.home),
url(r'^(?P<edition>[d] -[w] )/location/$', views.location),
  

В каждом из представлений я должен извлекать текущую версию. Дело в том, что если название редакции неверно, я хочу перенаправить на последнюю версию. Итак, я делаю что-то вроде

 def home(request, edition):
    try:
        event = Edition.objects.get(name=edition)
    except ObjectDoesNotExist:
        return redirect(home, edition=Edition.latest())
    # If event was found I go on here

def location(request, edition):
    try:
        event = Edition.objects.get(name=edition)
    except ObjectDoesNotExist:
        return redirect(home, edition=Edition.latest())
    # If event was found I go on here
  

и так далее. Конечно, есть некоторое дублирование, которое я хотел бы свести к минимуму. Я могу придумать два способа:

  • используйте get_objects_or_404() и настраивайте представление 404, или
  • абстрагируйте общую часть в функции.

Проблема с обоими способами заключается в том, что они не позволяют мне выполнить правильное перенаправление, то есть URL останется прежним, даже если представление было изменено. Есть ли лучший способ обработать эти перенаправления?

РЕДАКТИРОВАТЬ Кажется, мой вопрос неясен. В частности, неясно, что я подразумеваю под абстракцией общей части в функции. Итак, что я мог сделать, так это следующее

 def get_edition_or_current(edition):
    try:
        event = Edition.objects.get(name=edition)
    except ObjectDoesNotExist:
        event = Edition.latest()
    return event

def home(request, edition):
    event = get_edition_or_current(edition)
    # I go on with a valid event here

def location(request, edition):
    event = get_edition_or_current(edition)
    # I go on with a valid event here
  

Таким образом, я могу отобразить представление для правильного события, но я не могу изменить URL. Чтобы изменить URL, представление должно вернуть перенаправление. Я не могу установить возвращаемое значение для представления изнутри get_edition_or_current .

Итак, как Django реализует get_object_or_404 ? Ну, это просто, это вызывает исключение Http404 и перехватывает его позже. Но, конечно, это работает только для исключений Http404, потому что Django поручено их перехватывать.

Комментарии:

1. «абстрагировать общую часть в функции»? Почему бы вам этого не сделать? Настройка представления 404 включала бы всю дублированную логику, не так ли?

2. Чтобы изменить URL, я должен вернуть перенаправление в представлении. Если я вызвал someFunction() , я не могу вернуть перенаправление изнутри someFunction() . Мне пришлось бы проверить возвращаемое им значение и в случае необходимости вручную вернуть перенаправление, что ненамного лучше того, что я делаю сейчас.

3. @Andrea: «Если я вызвал someFunction(), я не могу вернуть перенаправление изнутри someFunction()»? Хорошо. Я озадачен тем, какое «дублирование», по вашему мнению, можно устранить с помощью «абстрагирования общей части в функции». Теперь вы говорите, что не можете этого сделать? Но в вопросе вы сказали, что могли бы? Пожалуйста, уточните.

4. Я мог бы устранить дублирование, например, путем извлечения последней версии, если данная отсутствует. Но я не смог бы отправить перенаправление. Разница заключалась бы в том, что URL-адрес не изменился бы.

5. Функции на самом деле разные, и дело не только в местоположении, их гораздо больше. Я не включил тело функций, поскольку оно не имеет отношения к рассматриваемой проблеме (абстрагируюсь от первой проверки работоспособности). Причина, по которой я включил примеры неработающих решений, заключалась в том, чтобы люди не публиковали их. Я пытался показать, как подходы naives не будут работать. В любом случае, я наконец нашел решение: смотрите мой ответ.

Ответ №1:

Я думаю, что самым простым способом сделать это было бы создать новую служебную функцию, вызываемую get_object_or_redirect в том же духе, что и get_object_or_404 . Вероятно, вы могли бы даже скопировать содержимое get_object_or_404 из django.shortcuts в качестве отправной точки для вашей реализации или просто извлечь то, что у вас есть выше.

РЕДАКТИРОВАТЬ: как отмечено в комментариях, перенаправление не может быть выполнено путем создания «исключения», так что это действительно не может работать так же, как get_object_or_404 .

Комментарии:

1. Проблема в том, что get_object_or_404 основан на исключениях, и я не уверен, что это правильный путь. Где я должен перехватывать исключения, которые я создаю? Django автоматически улавливает исключения Http404, но это частный случай.

2. Вот интересное обсуждение этого: groups.google.com/group/django-users/browse_thread/thread /…

3. Я не уверен, изменилось ли это, но, похоже, позиция django заключается в том, что перенаправление не является «исключительным», поэтому нет исключения HttpRedirectException — вы должны вернуть HttpRedirect из представления. Интересно, могли бы вы определить свое собственное исключение перенаправления, а затем обработать в пользовательском классе промежуточного программного обеспечения, который обрабатывает HTTP-ответ. промежуточное программное обеспечение на самом деле не слишком сложно реализовать, но я никогда не пытался иметь дело с исключениями и т.д. Другой идеей было бы реализовать промежуточное программное обеспечение для обработки ошибки 404, поступающей из метода view, и выполнить перенаправление туда.

4. Спасибо за интересную ссылку. Я нашел возможное решение, но оно похоже на взлом. Я могу следовать идеям, опубликованным там.

Ответ №2:

После еще некоторых размышлений я нашел решение. Достаточно, чтобы

  • используйте get_object_or_404
  • настройте представление 404, но не устанавливайте его в желаемый вид напрямую. Скорее, установите его в представление, которое отправит перенаправление на правильный вид.

Пример

 handler404 = views.error404

# Inside views

def error404(request):
    return redirect(...)
  

Комментарии:

1. 1 — Я думаю, что использование handler404, вероятно, является лучшим решением вашей проблемы. Также 1 за ваш вопрос — было интересно прочитать об этом!

2. Спасибо. В конце концов, я принял ваш ответ благодаря интересной ссылке для реализации Http-исключений с некоторым промежуточным программным обеспечением. Я не уверен, какое решение я в конечном итоге использую, но это, безусловно, приятное чтение.