6

I added django.contrib.auth.views.login everywhere in my webpage, for that I had to load a templatetag (that returns the AuthenticationForm) in my base.html. This templatetags includes the registration/login.html template.

The login is working ok but I want it to redirect the users to the same page they are before login. Now, it redirects me to /wherever_i_am/login wich shows registration/login.html with the 'login ok' or 'login fails' messages but without the rest of base.html.

I have followed django documentation and a few SO questions like this but I cannot redirect correctly. I have modified the next variable but it doesn't seem to work (next={{ request.get_full_path }} redirects me to /wherever_i_am/login ...again)

Have you tried something similar? any ideas?

UPDATE1 Now, the question could be something like: Do I have to declare my own login view if I want to include the login form everywhere in my web page?

Thank you.

juankysmith
  • 11,839
  • 5
  • 37
  • 62

9 Answers9

16

Found answer:

Change settings.LOGIN_REDIRECT_URL in your settings.py,

below code is copy from django:

   if request.method == "POST":
    form = authentication_form(data=request.POST)
    if form.is_valid():
        # Ensure the user-originating redirection url is safe.
        if not is_safe_url(url=redirect_to, host=request.get_host()):
            redirect_to = settings.LOGIN_REDIRECT_URL
   ...
Jiejing Zhang
  • 1,030
  • 8
  • 16
  • This is the code from django/contrib/aut/views.py:80 (Django 2.0). I am still trying this – swdev Jul 27 '18 at 15:30
6

The below allows redirects the user to the page they were attempting to access after they log in, but without having to write a custom view. It contains all the code you need to add to make it work. (As an aside, not all the TEMPLATE_CONTEXT_PROCESSORS are needed, but if you set a value to it explicitly you overwrite the defaults so need to re-add them.)

settings.py

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

urls.py

from django.contrib.auth.views import login, logout
...the other imports for your app ...

urlpatterns = patterns('',
    (r'^login/$', login, {'template_name':'login.html'} ),
    (r'^logout/$', logout,{'template_name':'logout.html'}),
    ...the other urls for your app...
)

login.html

<html>
    <form method="post" action="{% url 'django.contrib.auth.views.login' %}">
        {% csrf_token %}
        {{form}}<br/>
       <input type="submit" value="login" />
       <input type="hidden" name="next" value="{{ next }}" />
   </form>
</html>

logout.html

<html>
<p>You are logged out. To log in again, click <a href="/login/">here</a>.</p> 
</html>

views.py

@login_required(login_url="/login/")
def view1(request):
    ....this is a view you want to protect with security...

@login_required(login_url="/login/")
def view1(request):
    ....this is a view you want to protect with security...
sean
  • 520
  • 7
  • 17
2

I used something like this with default login view:

{% if form.errors %}
    <p class="error">Sorry, that's not a valid username or password</p>
{% endif %}

<form action="{% url login %}" method="post">
    {% csrf_token%}
    <label for="username">User name:</label>
    <input type="text" name="username" value="" id="username">
    <label for="password">Password:</label>
    <input type="password" name="password" value="" id="password">

    <input type="submit" value="login" />
    <input type="hidden" name="next" value="{{ request.get_full_path }}" />
</form>
    # or if it's not declareв шт urls:
     <form action="{% url django.contrib.auth.views.login %}?next={{ request.get_full_path }}" method="post">

everything worked fine.

PS: are you absolutely sure that "context_processors.request" is included in settings? Forgetting to include it is a common problem.

UPD: As far as I know, there are no way to make default login view to redirect on failed login (It just doesn't work that way).
Still i may be wrong

Pill
  • 4,815
  • 1
  • 24
  • 26
  • Yes, context_processors.request is included. I am not using input directly, I am using genenric AuthenticationForm ({{ form.username }}, ...) and my action="{% url django.contrib.auth.views.login %}" As I said, login works fine, the problem is in the redirection cause when login fails I see a (almost) blank template with only the '

    Sorry, that's not a valid username or password

    ' tag.
    – juankysmith Jul 07 '11 at 08:43
  • As i can see default login view redirects only on success. Thus to accomplish redirection with failed login you indeed need custom login view. – Pill Jul 07 '11 at 10:08
  • I can do that but I have been avoiding this solution from the begining cause I wanted to use only the generic django login. Am I force to declare my own login view?? – juankysmith Jul 07 '11 at 10:42
  • As far as i know - yes. But there are nothing criminal here - you want extended functionality - you extend it. – Pill Jul 07 '11 at 10:52
  • i think that in the hidden value is {{next}} not {{request.get_full_path}} – sacabuche Jul 25 '11 at 08:10
2

Finally I created a login view that calls django.contrib.auth.views.login internally.

juankysmith
  • 11,839
  • 5
  • 37
  • 62
  • Just import django.contrib.auth.views.login (maybe with an alias) and create a login function in your views.py, do whatever you have to do and called the contrib function at the end. – juankysmith Mar 01 '13 at 08:30
0

Adding up to @Sean's anwer. Code for iterating over each form field in order to write field error above the miss-typed field.

So, in Sean's login.html is the existing code:

login.html

<html>
    <form method="post" action="{% url 'django.contrib.auth.views.login' %}">
        {% csrf_token %}
        {{form}}<br/> <!-- I can change! -->
       <input type="submit" value="login" />
       <input type="hidden" name="next" value="{{ next }}" />
   </form>
</html>

Now what you should do is replace the "I can change!" line (4th line in the above code snippet) with following code:

{% for field in form %}

    <div class="form-group">
        <div class="col-sm-offset-2 col-sm-10">
            <span class="text-danger small"> {{ field.errors }}</span>
        </div>
        <label class="control-label col-sm-2">{{ field.label_tag }}</label>
        <div class="col-sm-10"> {{ field }}</div>
    </div>

{% endfor %}

You can use this snippet for other forms too (for example registration). :)

Filip Savic
  • 2,737
  • 1
  • 29
  • 34
0

I stumble upon this question in my process of implementing Facebook Account Linking. The problem is the same: how do I correctly redirect django after successful login?

Remember this: your settings.py contain LOGIN_REDIRECT_URL right? So, that's the only place where you should do the logic of redirecting. To do that, first connect this signal (put this in your views.py):

def after_success_login(sender, user, request, **kwargs):
   alt = request.GET.get('account_linking_token')
   if alt is not None:
       uri = request.GET.get('redirect_uri')
       request.session['fb_redirect_uri'] = uri
user_logged_in.connect(after_success_login)

The logic above may not reflect your case, but the idea is setting up a session variable to be read in the route defined as LOGIN_REDIRECT_URL.

So, in my case:

def index(request):
  if not request.user.is_authenticated():
    form = SignUpForm()
    return render(request, 'index.html', {'form': form})
  else:
    # FB ACCOUNT LINKING!
    if 'fb_redirect_uri' in request.session:
        redirect_uri = request.session['fb_redirect_uri']
        del request.session['fb_redirect_uri']
        to = '{}&authorization_code={}'.format(redirect_uri, request.user.username)
        print('to', to)
        return redirect(to)

That's it!

swdev
  • 4,997
  • 8
  • 64
  • 106
0

I'd suggest to pass a previous url as a parameter within the url:

/accounts/login/?next=my_previous_url

and then use this value in a view

request.next
Kee
  • 1,336
  • 11
  • 15
  • I am not declaring any login view cause I want to use django's django.contrib.auth.views.login itself. I have tried 'login/?next=...' by getting and sending 'my_previous_url' from my templatetag but it always redirects me to /wherever_i_am/login :( Am I forced to declare a login view??? – juankysmith Jul 07 '11 at 07:20
0

{{request.get_full_path}} gives you the current path, so is normal that the redirect points to the same place, change it for {{next}} in your registration/login.html template

sacabuche
  • 2,781
  • 1
  • 27
  • 35
-5

Add a decorator before the view function should be OK.

@login_required

see here for details

ChanDon
  • 391
  • 1
  • 5
  • 15
  • @juankysmith: Sure it does. the decorator directs the user to `/accounts/login/?next=my_previous_url` – ChanDon Jul 28 '11 at 09:51
  • Sorry, maybe my explanation is not clear. I included a templatetag to have the login form everywhere on my webpage, the classic bar under the header, so I want to be redirected to the same page I actually am. I finally created a login view that calls django.contrib's login that redirects to request.META.get('HTTP_REFERER') – juankysmith Jul 28 '11 at 10:14
  • @juankysmith may be you should put your login in the base.html, i think that a templatetag is no neccesary, have you tried my answer? – sacabuche Aug 02 '11 at 02:53
  • I am not using the next parameter – juankysmith Aug 02 '11 at 05:50