czym jest reverse () w Django

219

Kiedy czasami czytam kod django, widzę w niektórych szablonach reverse(). Nie jestem do końca pewien, co to jest, ale jest używane razem z HttpResponseRedirect. Jak i kiedy należy to reverse()wykorzystać?

Byłoby miło, gdyby ktoś dał odpowiedź z kilkoma przykładami ...

lakesh
źródło
26
Biorąc pod uwagę wzorzec adresu URL, Django używa url (), aby wybrać odpowiedni widok i wygenerować stronę. Oznacza to, że url--> view name. Ale czasami, tak jak podczas przekierowywania, musisz iść w odwrotnym kierunku i podać Django nazwę widoku, a Django generuje odpowiedni adres URL. Innymi słowy view name --> url. To znaczy reverse()(jest odwrotnością funkcji url). Nazwanie go może wydawać się bardziej przejrzyste, generateUrlFromViewNameale jest to zbyt długie i prawdopodobnie niewystarczająco ogólne: docs.djangoproject.com/en/dev/topics/http/urls/…
eric
4
@neuronet Świetne wyjaśnienie, dzięki. Ta nazwa wydawała mi się (i wydaje się) szczególnie nieintuicyjna, co uważam za poważny grzech. Kto nie nienawidzi niepotrzebnego zaciemniania?
Mike
Jest to typowy przykład nazywania, który podkreśla jeden aspekt bytu (np. Funkcji), który w tym czasie był przede wszystkim w umyśle programisty, biorąc pod uwagę jego kontekst, ale nie jest najbardziej przydatną opcją w szerokim kontekście jakiegokolwiek innego programisty . Często wpadamy w tę pułapkę jako programiści - nazewnictwo jest tak ważne dla wykrywalności, że warto zatrzymać się i pomyśleć o różnych kontekstach i wybrać najbardziej odpowiedni.
Cornel Masson

Odpowiedzi:

347

reverse()| Dokumentacja Django


Załóżmy, że urls.pyzdefiniowałeś to w swoim:

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

W szablonie możesz następnie odwołać się do tego adresu URL jako:

<!-- 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>

Będzie to renderowane jako:

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

Teraz powiedz, że chcesz zrobić coś podobnego w swoim views.py- np. Masz do czynienia z innym adresem URL (nie /foo/) w innym widoku (nie some_view) i chcesz przekierować użytkownika /foo/(często w przypadku pomyślnego przesłania formularza).

Możesz po prostu zrobić:

return HttpResponseRedirect('/foo/')

Ale co, jeśli chcesz zmienić adres URL w przyszłości? Będziesz musiał zaktualizować swoje urls.py i wszystkie odniesienia do niego w kodzie. To narusza DRY (Don't Repeat Yourself) , cały pomysł edytowania tylko jednego miejsca, do czego należy dążyć.

Zamiast tego możesz powiedzieć:

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

Przegląda wszystkie adresy URL zdefiniowane w projekcie pod kątem adresu URL zdefiniowanego za pomocą nazwy url_namei zwraca rzeczywisty adres URL /foo/.

Oznacza to, że odwołujesz się do adresu URL tylko za pomocą jego nameatrybutu - jeśli chcesz zmienić sam adres URL lub widok, którego dotyczy, możesz to zrobić, edytując tylko jedno miejsce - urls.py.

scytale
źródło
3
Do Twojej wiadomości, {{ url 'url_name' }}powinien być {% url url_name %}w Django 1.4 lub wcześniejszym. Zmieni się to w następnej wersji Django (1.5) i powinno być {% url 'url_name' %}. Dokumenty dotyczące szablonu
adresu
1
j_syk dzięki - robiłem @load url z future @ odkąd 1.3 wyszło i zapomniałem, że to jeszcze nie jest domyślny. Zaktualizuję moją odpowiedź, aby nie zaskoczyła niedoświadczonego.
scytale
2
naprawiono - myślę, że edytowanie głupich literówek w odpowiedziach innych osób jest całkowicie dopuszczalne, więc jeśli zobaczysz więcej, po prostu wskocz :-)
scytale
3
Jedna z najbardziej subtelnych odpowiedzi, jakie można znaleźć na tej stronie.
Manas Chaturvedi
1
„>>> ale co, jeśli chcesz zmienić adres URL w przyszłości”, tego rodzaju subtelności są przydatne przez 0,0001% czasu, a rozwiązanie jest dostarczane jak przydatna funkcja, a ludzie używają go tak, jakby to było „ najlepsze praktyki ”i zostaw bałagan. TBH, jeśli jeśli w przyszłości zmienisz adresy URL, po prostu wykonasz globalne wyszukiwanie-zamień. Nawet to rozwiązanie (użyj nazwy url) jest podatne na problem „co jeśli chcesz zmienić nazwę url w przyszłości?” Koduję w Django od ponad 5 lat i jeszcze nie spełniam wymagań url_reverse. Najlepszym sposobem radzenia sobie z tego rodzaju dziwactwami jest odmowa ich użycia.
nehem
10

To stare pytanie, ale jest coś, co może komuś pomóc.

Z oficjalnych dokumentów:

Django zapewnia narzędzia do wykonywania odwracania adresów URL, które pasują do różnych warstw, w których potrzebne są adresy URL: W szablonach: Używanie tagu szablonu adresu URL. W kodzie Python: Korzystanie z funkcji reverse (). W kodzie wyższego poziomu związanym z obsługą adresów URL instancji modelu Django: metoda get_absolute_url ().

Na przykład. w szablonach (tag url)

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

Na przykład. w kodzie python (przy użyciu reversefunkcji)

return HttpResponseRedirect(reverse('news-year-archive', args=(year,)))
Kishy Nivas
źródło
1
potrzebuję pełnego opisu szefie
dajJob
OP konkretnie wspomniał, że czytał dokumenty, potrzebował wyjaśnień, a nie tylko kopiowania / wklejania z dokumentów.
RusI
8

Istniejące odpowiedzi świetnie spisały się wyjaśniając, co z tą reverse()funkcją w Django.

Miałem jednak nadzieję, że moja odpowiedź rzuci inne światło na to, dlaczego : dlaczego używać reverse()zamiast innych, bardziej prostych, prawdopodobnie bardziej pythonowych podejść w wiązaniu widoku szablonu, i jakie są uzasadnione powody popularności tego przekierowania za pośrednictwem reverse() wzorzec ”w logice routingu Django.

Jedną z kluczowych korzyści jest odwrotna konstrukcja adresu URL, jak wspomniali inni. Podobnie jak chcesz {% url "profile" profile.id %}wygenerować adres URL z pliku konfiguracyjnego adresu URL aplikacji: np path('<int:profile.id>/profile', views.profile, name="profile").

Ale, jak zauważył PO, użycie reverse()jest również często łączone z użyciem HttpResponseRedirect. Ale dlaczego?

Nie jestem do końca pewien, co to jest, ale jest używane razem z HttpResponseRedirect. Jak i kiedy ma być używana funkcja reverse ()?

Rozważ następujące kwestie 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)
        ))

A nasze minimalne 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')
]

W vote()funkcji kod w naszym elsebloku używa reversewraz z HttpResponseRedirectnastępującym wzorem:

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

To przede wszystkim oznacza, że ​​nie musimy kodować adresu URL na stałe (zgodnie z zasadą DRY), ale co bardziej istotne, reverse()zapewnia elegancki sposób konstruowania ciągów adresów URL poprzez obsługę wartości rozpakowanych z argumentów ( args=(question.id)jest obsługiwany przez URLConfig). Nałożony questionma atrybut, idktóry zawiera wartość 5, adres URL skonstruowany na podstawie reverse()będzie wtedy:

'/polls/5/results/'

W normalnym kodzie wiązania widoku szablonu używamy HttpResponse()lub, render()ponieważ zazwyczaj wymagają one mniej abstrakcji: jedna funkcja widoku zwraca jeden szablon:

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

Ale w wielu uzasadnionych przypadkach przekierowania zazwyczaj zależy nam na zbudowaniu adresu URL z listy parametrów. Obejmują one przypadki takie jak:

  • Przesyłanie formularza HTML za pośrednictwem POSTżądania
  • Potwierdzenie logowania użytkownika
  • Zresetuj hasło za pomocą tokenów internetowych JSON

Większość z nich wymaga przekierowania i adresu URL zbudowanego na podstawie zestawu parametrów. Mam nadzieję, że to dodaje do już pomocnego wątku odpowiedzi!

tylko fantom
źródło
4

Ta funkcja obsługuje zasadę „sucha” - zapewniając, że nie kodujesz adresów URL w całej aplikacji. Adres URL powinien być zdefiniowany w jednym miejscu i tylko w jednym miejscu - twój adres URL. Potem tak naprawdę po prostu odwołujesz się do tych informacji.

Użyj, reverse()aby podać adres URL strony, podając ścieżkę do widoku lub parametr nazwa_strony z konf. Adresu URL. Używałbyś go w przypadkach, gdy nie ma sensu robić tego w szablonie {% url 'my-page' %}.

Istnieje wiele możliwych miejsc, w których możesz użyć tej funkcji. Korzystam z tego miejsca, gdy przekierowuję użytkowników w widoku (często po udanym przetworzeniu formularza) -

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

Możesz go również użyć podczas pisania znaczników szablonów.

Innym razem użyłem reverse()dziedziczenia modelu. Miałem ListView na modelu nadrzędnym, ale chciałem dostać się z jednego z tych obiektów nadrzędnych do DetailView powiązanego obiektu podrzędnego. I załączeniu get__child_url()funkcji do rodzica, który zidentyfikowano istnienie dziecka i wrócił url To DetailView użyciu reverse().

Ashish Kumar Sahoo
źródło
2

Istniejące odpowiedzi są dość jasne. Na wszelki wypadek, gdy nie wiesz, dlaczego się nazywa reverse: pobiera nazwę adresu URL i podaje rzeczywisty adres URL, co jest odwrotne do posiadania adresu URL, a następnie nadania mu nazwy.

yyFred
źródło
1
Po prostu uczę się Django z samouczka (Django Girls). To stroma krzywa uczenia się. Myślę, że nazwa tej funkcji jest okropna: „Zarezerwuj” bez żadnych kwalifikacji BARDZO MOCNY sugeruje zarezerwowanie listy lub łańcucha, który oczywiście nie ma z tym nic wspólnego.
Mike gryzoń
@mikerodent Całkowicie się z tobą zgadzam. Poza tym żadna z tych odpowiedzi nie wyjaśnia, dlaczego funkcja jest nazywana odwrotną. To takie złe imię imo.
Soham Dongargaonkar
1

Funkcja reverse () służy do przestrzegania zasady DRY django DRY, tzn. Jeśli zmienisz adres URL w przyszłości, możesz odwoływać się do tego adresu URL za pomocą reverse (urlname).

AEROCODE
źródło