Django: Przekieruj do poprzedniej strony po zalogowaniu

175

Próbuję zbudować prostą stronę internetową z funkcjonalnością logowania bardzo podobną do tej tutaj na SO. Użytkownik powinien mieć możliwość przeglądania witryny jako użytkownik anonimowy, a na każdej stronie będzie znajdował się link do logowania. Po kliknięciu łącza logowania użytkownik zostanie przeniesiony do formularza logowania. Po pomyślnym zalogowaniu użytkownik powinien zostać przeniesiony z powrotem na stronę, z której w pierwszej kolejności kliknął link logowania. Zgaduję, że muszę jakoś przekazać adres URL bieżącej strony do widoku obsługującego formularz logowania, ale tak naprawdę nie mogę go uruchomić.

EDYCJA: Rozgryzłem to. Połączyłem się z formularzem logowania, przekazując bieżącą stronę jako parametr GET, a następnie użyłem „next”, aby przekierować na tę stronę. Dzięki!

EDYCJA 2: Moje wyjaśnienie nie wydawało się jasne, więc zgodnie z żądaniem jest mój kod: Powiedzmy, że jesteśmy na stronie foo.html i nie jesteśmy zalogowani. Teraz chcielibyśmy mieć link na foo.html, który zawiera linki to login.html. Tam możemy się zalogować, a następnie zostajemy przekierowani z powrotem do foo.html. Link na foo.html wygląda następująco:

      <a href='/login/?next={{ request.path }}'>Login</a> 

Teraz napisałem niestandardowy widok logowania, który wygląda mniej więcej tak:

def login_view(request):
   redirect_to = request.REQUEST.get('next', '')
   if request.method=='POST':
      #create login form...
      if valid login credentials have been entered:
         return HttpResponseRedirect(redirect_to)  
   #...
   return render_to_response('login.html', locals())

I ważny wiersz w login.html:

<form method="post" action="./?next={{ redirect_to }}">

Więc tak, to prawie wszystko, mam nadzieję, że to wyjaśnia.

jörg
źródło
1
Myślę, że logowanie w Django załatwia to dla ciebie, czy to nie działa dla ciebie?
Dominic Rodger

Odpowiedzi:

151

Nie musisz w tym celu tworzyć dodatkowego widoku, funkcjonalność jest już wbudowana.

Najpierw każda strona z łączem logowania musi znać aktualną ścieżkę, a najłatwiejszym sposobem jest dodanie preprosesora kontekstu żądania do settings.py (pierwsze 4 są domyślne), wtedy obiekt żądania będzie dostępny w każdym żądaniu:

settings.py:

TEMPLATE_CONTEXT_PROCESSORS = (
    "django.core.context_processors.auth",
    "django.core.context_processors.debug",
    "django.core.context_processors.i18n",
    "django.core.context_processors.media",
    "django.core.context_processors.request",
)

Następnie dodaj szablon, do którego chcesz dodać link do logowania:

base.html:

<a href="{% url django.contrib.auth.views.login %}?next={{request.path}}">Login</a>

Spowoduje to dodanie argumentu GET do strony logowania, który wskazuje na bieżącą stronę.

Szablon logowania może być wtedy tak prosty:

rejestracja / login.html:

{% block content %}
<form method="post" action="">
  {{form.as_p}}
<input type="submit" value="Login">
</form>
{% endblock %}
sverrejoh
źródło
47
Osobiście korzystam z tego rozwiązania, ale jedyną modyfikacją, jaką bym wprowadził, jest to, że nie powiedzie się, jeśli request.pathjest zerowe, więc robię to {% firstof request.path '/' %}w ten sposób, jeśli ścieżka żądania nie jest dostępna z jakiegoś powodu, użytkownik zostanie wysłany na stronę główną.
jorelli
1
Próbuję zaimplementować Twoje rozwiązanie, ale gdy django.contrib.auth.views.login przekierowuje (po pomyślnym uwierzytelnieniu) wystąpienie użytkownika nie znajduje się w żądaniu, więc user.is_authenticated () zawsze zwraca False. Jakaś pomoc?
juankysmith
3
Dodając do @jorelli, możesz użyć nazwanego adresu URL i <a href="{% url auth_login %}?next={% firstof request.path '/' %}">Login</a>zamiast tego zrobić .
hobbes3
12
Trzeba też uważać na to, czy użytkownik zaloguje się już po wylogowaniu. Innymi słowy, nextbyłoby to coś podobnego /accounts/logout/i po zalogowaniu się użytkownika zostanie on natychmiast wylogowany z powrotem lol, a pętla będzie kontynuowana.
hobbes3
2
Okazało się, że to zadziałało tylko wtedy, gdy dodałem następujące polecenie do formularza logowania w Django 1.5: <input type = "hidden" name = "next" value = "{{next}}" />
Ellis Percival
28

Może to nie jest „najlepsza praktyka”, ale z powodzeniem stosowałem to wcześniej:

return HttpResponseRedirect(request.META.get('HTTP_REFERER','/'))
Dotacja
źródło
10
Ma to słaby punkt - część użytkowników / przeglądarek ma wyłączonego sędziego podającego
Tomasz Zieliński
4
Istnieje również problem w przypadku, gdy użytkownik dociera do formularza, wypełnia informacje, a następnie przesyła. HTTP_REFERER jest wtedy adresem URL strony formularza, a nie adresem URL strony, z której przyszedł użytkownik.
LaundroMat
25

Aby obsługiwać pełne adresy URL z parametrami / wartościami, których potrzebujesz:

?next={{ request.get_full_path|urlencode }}

zamiast tylko:

?next={{ request.path }}
arntg
źródło
Właściwie to nie jest dobry pomysł, ciąg zapytania będzie bardzo długi, jeśli masz zbyt wiele kliknięć
dspjm
14

Wbudowane uwierzytelnianie Django działa tak, jak chcesz.

Ich strony logowania zawierają nextciąg zapytania, do którego należy powrócić po zalogowaniu.

Spójrz na http://docs.djangoproject.com/en/dev/topics/auth/#django.contrib.auth.decorators.login_required

S.Lott
źródło
4
Cóż, dekorator login_required działałby dobrze, ponieważ ustawia `` obok '' dowolnej strony, z której pochodzę, ale podczas korzystania z dekoratora anonimowi użytkownicy nie mogą przeglądać witryny, ponieważ będą od razu przekierowywani do formularza logowania. Myślę, że muszę tylko dowiedzieć się, jak ustawić wartość „next” w adresie URL strony, z której pochodzi użytkownik.
jörg
1

Połączyłem się z formularzem logowania, przekazując bieżącą stronę jako parametr GET, a następnie użyłem „next”, aby przekierować na tę stronę. Dzięki!

jörg
źródło
1

Napotkałem ten sam problem. To rozwiązanie pozwala mi nadal używać ogólnego widoku logowania:

urlpatterns += patterns('django.views.generic.simple',
    (r'^accounts/profile/$', 'redirect_to', {'url': 'generic_account_url'}),
)
muczeć
źródło
1

W registration/login.html(zagnieżdżone w templatesfolderze), jeśli wstawisz następujący wiersz, strona będzie wyglądać jak oryginalna strona logowania administratora Django:

{% include "admin/login.html" %}

Uwaga: plik powinien zawierać tylko powyższe wiersze.

Harshith JV
źródło
-1

Zobacz dokumentację django dla views.login () , podajesz wartość 'next' (jako ukryte pole) w formularzu wejściowym, do którego ma nastąpić przekierowanie po pomyślnym zalogowaniu.

YHVH
źródło
Tak, ale jak właściwie ustawić „obok” strony, z której pochodzę?
jörg
-3

Ty też możesz to zrobić

<input type="hidden" name="text" value="{% url 'dashboard' %}" />
JREAM
źródło
Zła odpowiedź, ale nazwa = 'next' wydaje się wykonalna, nie została przetestowana.
WeizhongTu