#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/
и так далее. Существуют различные страницы (например, 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-исключений с некоторым промежуточным программным обеспечением. Я не уверен, какое решение я в конечном итоге использую, но это, безусловно, приятное чтение.