що є зворотним () у Django


219

Коли я читаю код джанго іноді, я бачу в деяких шаблонах reverse(). Я не зовсім впевнений, що це, але він використовується разом з HttpResponseRedirect. Як і коли це reverse()слід використовувати?

Було б добре, якби хтось дав відповідь якимись прикладами ...


26
З огляду на шаблон URL-адреси, Django використовує url () для вибору потрібного перегляду та створення сторінки. Тобто url--> view name. Але іноді, як, наприклад, під час переадресації, потрібно йти у зворотному напрямку та надати Джанго ім'я перегляду, і Джанго генерує відповідний URL. Іншими словами, view name --> url. Тобто reverse()(це зворотна функція url). Це може здатися більш прозорим, щоб просто назвати це, generateUrlFromViewNameале це занадто довго і, мабуть, недостатньо загально: docs.djangoproject.com/en/dev/topics/http/urls/…
eric

4
@neuronet Чудове пояснення, дякую. Це ім'я здавалося мені (і здається) особливо неінтуїтивним, що я вважаю важким гріхом. Хто не ненавидить зайвих примхливості?
мійський гризун

Це типовий приклад називання, який підкреслює один аспект сутності (наприклад, функцію), який був головним у розумі програміста в той час, враховуючи його контекст, але не є найбільш корисним варіантом у широкому контексті будь-якого іншого розробника . Ми часто потрапляємо в цю пастку, як програмісти - називання настільки важливе для відкриття, що варто зупинитися і подумати над різними контекстами та вибрати найбільш відповідний.
Корнел Массон

Відповіді:


347

reverse()| Документація Джанго


Припустимо, що у ваших urls.pyви визначили це:

url(r'^foo$', some_view, name='url_name'),

Потім у шаблоні ви можете посилатися на цей URL як:

<!-- django <= 1.4 -->
<a href="{% url url_name %}">link which calls some_view</a>

<!-- django >= 1.5 or with {% load url from future %} in your template -->
<a href="{% url 'url_name' %}">link which calls some_view</a>

Це відображатиметься як:

<a href="/foo/">link which calls some_view</a>

Тепер скажіть, що ви хочете зробити щось подібне у своєму views.py- наприклад, ви обробляєте якийсь інший URL (не /foo/) в якомусь іншому перегляді (не some_view), і ви хочете перенаправити користувача /foo/(часто це відбувається при успішному поданні форми).

Ви можете просто зробити:

return HttpResponseRedirect('/foo/')

Але що робити, якщо ви хочете змінити URL-адресу в майбутньому? Вам доведеться оновити свій urls.py та всі посилання на нього у своєму коді. Це порушує DRY (не повторюйте себе) , всю ідею редагування лише одного місця, до чого слід прагнути.

Натомість ви можете сказати:

from django.urls import reverse
return HttpResponseRedirect(reverse('url_name'))

Це переглядає всі URL-адреси, визначені у вашому проекті, для URL-адреси, визначеної іменем, url_nameі повертає фактичну URL-адресу /foo/.

Це означає, що ви посилаєтесь на URL лише за його nameатрибутом - якщо ви хочете змінити сам URL або вигляд, на який він посилається, ви можете зробити це, відредагувавши лише одне місце - urls.py.


3
FYI, {{ url 'url_name' }}має бути {% url url_name %}в Django 1.4 або раніше. Це зміниться в наступному випуску Django (1.5), а потім має бути {% url 'url_name' %}. Документи для тегів шаблону URL-адреси дають корисну інформацію, якщо трохи прокрутити вниз до розділу "сумісність вперед"
j_syk

1
j_syk дякую - я робив @load url з майбутнього @ з 1.3 вийшов і забув, що це ще не за замовчуванням. Я оновлю свою відповідь, щоб вона не обходила недосвідчених.
scytale

2
виправлено - я вважаю, що вам цілком прийнятно редагувати тупі помилки у відповідях інших людей, тому якщо ви бачите більше, просто
заскочіть

3
Один з найтонших відповідей, який можна було знайти на цьому сайті.
Манас Чатурведі

1
">>> але що робити, якщо ви хочете змінити URL-адресу в майбутньому", ці тонкощі, які корисні в .0001% часу, і рішення постачається як корисна функція, і люди використовують його так, ніби вони є " кращі практики »і залишають безлад. TBH, якщо колись змінити URL-адреси в майбутньому, ти просто робиш глобальну пошук-заміну. Навіть це рішення (використовуйте url_name) схильне до проблеми "що робити, якщо ви хочете змінити url_name у майбутньому?" Кодування в Джанго проводилося більше 5 років і все ж для задоволення потреби url_reverse. Найкращий спосіб подолати подібні дивацтва - це відмовитися від їх використання.
nehem

10

Це давнє запитання, але ось щось може допомогти комусь.

З офіційних документів:

Django надає інструменти для здійснення зворотного перегляду URL-адрес, які відповідають різним шарам, де потрібні URL-адреси: У шаблонах: Використання тегу шаблону URL-адреси. У коді Python: Використання функції reverse (). У коді вищого рівня, пов'язаному з обробкою URL-адрес екземплярів моделі Django: метод get_absolute_url ().

Напр. у шаблонах (тег URL)

<a href="{% url 'news-year-archive' 2012 %}">2012 Archive</a>

Напр. в коді python (за допомогою reverseфункції)

return HttpResponseRedirect(reverse('news-year-archive', args=(year,)))

1
потрібен повний опис боса
giveJob

ОП спеціально зазначив, що він читав документи, йому потрібні пояснення, а не просто копіювання / вставлення з документів.
РусьІ

8

Існуючі відповіді зробили чудову роботу в поясненні того, що ця reverse()функція в Джанго.

Однак я сподівався, що моя відповідь проливає інше світло на те, чому : чому використовувати reverse()замість інших більш прямих, можливо більш пітонічних підходів у прив'язці перегляду шаблонів, і які є законні причини для популярності цього "переадресації через reverse() шаблон "в логіці маршрутизації Джанго.

Однією з головних переваг є зворотна побудова URL-адреси, як згадували інші. Так само, як би ви використовували {% url "profile" profile.id %}для створення URL-адреси з файлу конфігурації URL-адреси програми: наприклад path('<int:profile.id>/profile', views.profile, name="profile").

Але, як зазначають ОП, використання reverse()також зазвичай поєднується із використанням HttpResponseRedirect. Але чому?

Я не зовсім впевнений, що це, але він використовується разом з HttpResponseRedirect. Як і коли передбачається використовувати цей зворотний ()?

Розглянемо наступне views.py:

from django.http import HttpResponseRedirect
from django.urls import reverse

def vote(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    try:
        selected = question.choice_set.get(pk=request.POST['choice'])
    except KeyError:
        # handle exception
        pass
    else:
        selected.votes += 1
        selected.save()
        return HttpResponseRedirect(reverse('polls:polls-results',
                                    args=(question.id)
        ))

І наш мінімальний urls.py:

from django.urls import path
from . import views

app_name = 'polls'
urlpatterns = [
    path('<int:question_id>/results/', views.results, name='polls-results'),
    path('<int:question_id>/vote/', views.vote, name='polls-vote')
]

У vote()функції код у нашому elseблоці використовується reverseпоряд із HttpResponseRedirectнаступною схемою:

HttpResponseRedirect(reverse('polls:polls-results',
                                        args=(question.id)

Це, перш за все, означає, що нам не доведеться жорстко кодувати URL (відповідно до принципу DRY), але більш важливо reverse()- це елегантний спосіб побудови рядків URL, обробляючи значення, розпаковані з аргументів ( args=(question.id)обробляється URLConfig). Припустимо question, є атрибут, idякий містить значення 5, URL-адреса, побудована з reverse()цього, буде:

'/polls/5/results/'

У звичайному коді прив'язки перегляду шаблонів ми використовуємо HttpResponse()або, render()як правило, вони передбачають меншу абстракцію: одна функція перегляду повертає один шаблон:

def index(request):
    return render(request, 'polls/index.html') 

Але у багатьох законних випадках перенаправлення ми, як правило, піклуємося про побудову URL-адреси зі списку параметрів. Сюди входять такі випадки, як:

  • Подання форми HTML через POSTзапит
  • Підтвердження входу користувача
  • Скидання пароля через веб-маркери JSON

Більшість із них передбачає певну форму перенаправлення та URL-адресу, побудовану за допомогою набору параметрів. Сподіваюсь, це додасть уже корисну нитку відповідей!


4

Ця функція підтримує сухий принцип - гарантування того, що у вас не буде жорстких URL-адрес коду. URL-адресу слід визначити в одному місці, і лише одне місце - ваш конфлікт URL-адреси. Після цього ви дійсно просто посилаєтесь на цю інформацію.

Використовуйте, reverse()щоб надати URL-адресу сторінки, вказавши або шлях до перегляду, або параметр_іменник_ сторінки з вашої URL-адреси. Ви б використовували його в тих випадках, коли не має сенсу робити це за допомогою шаблону {% url 'my-page' %}.

Є багато можливих місць, де ви можете використовувати цю функціональність. Я знайшов одне місце, яке я використовую, коли переспрямовують користувачів у перегляді (часто після успішної обробки форми) -

return HttpResponseRedirect(reverse('thanks-we-got-your-form-page'))

Ви також можете використовувати його під час написання тегів шаблонів.

Інший раз, коли я користувався, reverse()був з успадкуванням моделі. Я мав ListView на батьківській моделі, але хотів потрапити з будь-якого з цих батьківських об'єктів до DetailView асоційованого дочірнього об’єкта. Я приєднав get__child_url()функцію до батьків, яка визначила існування дитини та повернула URL-адресу її DetailView за допомогою reverse().



2

Існуючі відповіді цілком зрозумілі. На випадок, якщо ви не знаєте, чому він викликається reverse: він бере введення імені URL-адреси і видає фактичну URL-адресу, яка є зворотною для того, щоб спочатку мати URL-адресу, а потім дати йому ім'я.


1
Тільки-но вивчивши Джанго з підручника (Django Girls). Це крута крива навчання. Я думаю, що назва цієї функції є жахливим: "резерв" без будь-якої кваліфікації ДУЖЕ СИЛЬНО пропонує запропонувати список або рядок, який, очевидно, не має нічого спільного з цим.
гризун

@mikerodent Я повністю з вами згоден. Крім того, жодна з цих відповідей не пояснює, чому функція називається зворотною. Це таке недобре ім'я imo.
Soham Dongargaonkar

1

Зворотний () використовується для дотримання принципу DRY джанго, тобто якщо ви зміните URL-адресу в майбутньому, ви можете посилатися на цей URL-адресу, використовуючи зворотний (urlname).

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.