django redirect after login not working "next" not posting?

Question:

I have a login page which is working fine with the exception of the redirect to the referrer page. The user gets an email with a direct link within the app, they (in this example) are not logged in already and are redirected to the login page. After successful login the user is redirected to a hardcoded path. See example below.

URL in email: http://localhost:8000/issueapp/1628/view/22

URL of login page: http://localhost:8000/login?next=/issueapp/1628/view/22

Login view (with hardcoded redirect):

def login_user(request):    
    state = "Please log in below..."
    username = password = ''

    if request.POST:
        username = request.POST['username']
        password = request.POST['password']

        user = authenticate(username=username, password=password)
        if user is not None:
            if user.is_active:
                login(request, user)
                state = "You're successfully logged in!"
                return HttpResponseRedirect('/issueapp/1628/')
            else:
                state = "Your account is not active, please contact the site admin."
        else:
            state = "Your username and/or password were incorrect."

    return render_to_response(
        'account_login.html',
        {
        'state':state,
        'username': username
        },
        context_instance=RequestContext(request)
    )

Login view (with “next” redirect):

def login_user(request):    
    state = "Please log in below..."
    username = password = ''

    if request.POST:
        username = request.POST['username']
        password = request.POST['password']

        user = authenticate(username=username, password=password)
        if user is not None:
            if user.is_active:
                login(request, user)
                state = "You're successfully logged in!"
                return HttpResponseRedirect(request.GET['next'])
            else:
                state = "Your account is not active, please contact the site admin."
        else:
            state = "Your username and/or password were incorrect."

    return render_to_response(
        'account_login.html',
        {
        'state':state,
        'username': username
        },
        context_instance=RequestContext(request)
    )

The above view results in an exception "Key 'next' not found in <QueryDict: {}>" The form does not appear to be posting the “next” variable, even though its there in the url and in the form.
I have searched and looked everywhere and cant figure out why its not working. Any ideas?

Additional reference:

Login template:

{% block content %}

    {{ state }}
    <form action="/login/" method="post" >
                {% csrf_token %}
        {% if next %}
        <input type="hidden" name="next" value="{{ next }}" />
        {% endif %}
        username:
        <input type="text" name="username" value="{{ username }}" /><br />
        password:
        <input type="password" name="password" value="" /><br />

        <input type="submit" value="Log In"/>

        {% debug %}
    </form>
{% endblock %}

EDIT: The below is the code which is now working for me (thanks to the help of Paulo Bu)! **

Login View:

def login_user(request):

    state = "Please log in below..."
    username = password = ''

    next = ""

    if request.GET:  
        next = request.GET['next']

    if request.POST:
        username = request.POST['username']
        password = request.POST['password']

        user = authenticate(username=username, password=password)
        if user is not None:
            if user.is_active:
                login(request, user)
                state = "You're successfully logged in!"
                if next == "":
                    return HttpResponseRedirect('/issueapp/')
                else:
                    return HttpResponseRedirect(next)
            else:
                state = "Your account is not active, please contact the site admin."
        else:
            state = "Your username and/or password were incorrect."

    return render_to_response(
        'account_login.html',
        {
        'state':state,
        'username': username,
        'next':next,
        },
        context_instance=RequestContext(request)
    )

Login Template:

{{ state }}

{% if next %}
<form action="/login/?next={{next}}" method="post" >
{%else%}
<form action="/login/" method="post" >
{% endif %}

            {% csrf_token %}

    username:
    <input type="text" name="username" value="{{ username }}" /><br />
    password:
    <input type="password" name="password" value="" /><br />

    <input type="submit" value="Log In"/>

    {% debug %}
</form>
Asked By: James

||

Answers:

Your code is fine, the only problem is that in the form you are passing the next attribute as a post because the method is post. In the views you try to get the next parameter within the get dictionary which is obvious not there.

You have to declare the html form action like this in order to your views work.

{% if next %}
<form action="/login/?next={{next}}" method="post" >
{%else%}
<form action="/login/" method="post" >
{% endif %}
        {% csrf_token %}
        username:
        <input type="text" name="username" value="{{ username }}" /><br />
        password:
        <input type="password" name="password" value="" /><br />

        <input type="submit" value="Log In"/>

        {% debug %}
    </form>

There, if there is a next variable then you include in the url for retrieve it as a get parameter. If not, the form doesn’t include it.

This is the best approach, but you may also fix this in your views by requesting the next from the POST dictionary like this:

return HttpResponseRedirect(request.POST.get('next'))

Note that this will only work if the template account_login has a variable called next. You should generate it in the views and pass it to the template when you render it.

Normally, in the template you would do something like this:

# this would be hardcoded
next = '/issueapp/1628/view/22'
# you may add some logic to generate it as you need.

and then you do:

return render_to_response(
    'account_login.html',
    {
    'state':state,
    'username': username,
    'next':next
    },
    context_instance=RequestContext(request)
)

Hope this helps!

Answered By: Paulo Bu

If you want to be more generic, you could just do something like this, which passes any of the GET parameters along when the form is posted:

<form action="/path-to-whatever/{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" method="post">
Answered By: seddonym

Instead of assigning next in your view & passing it to template, isn’t it cleaner to use ?next={{request.path}} in your template. (Remember to enable django.core.context_processors.request in settings.py, which is usually enabled in by default in django 1.6)

Here is the link tells about the same

https://docs.djangoproject.com/en/1.6/topics/auth/default/#the-raw-way

<form action="/login/?next={{request.path}}" method="post" >

is the code required.

Note:

You can get the current url with request.path from view also.

Thanks to buffer. I have just copy and pasted your comment after trying it myself in my own code.

Answered By: Ajeeb.K.P

Just put

<form action="" method="post" >

Empty action ‘what ever current complete url is

Answered By: Rizwan Mumtaz

In short

I would define in your view function next_page = request.GET['next'] and then redirect to it by return HttpResponseRedirect(next_page) so you never need to change templates; just set @login_required and you are fine.

As example:

User A tries to access – while not logged in – https://www.domain.tld/account/. Django redirects him because @login_required is set to the defined LOGIN_URL in your settings.py. The method UserLogin now tries to GET the next parameter and redirects to it if user A logs in successfully.

settings.py

LOGIN_URL = '/login/'

urls.py

url(r'^account/', account, name='account'),
url(r'^login/$', UserLogin, name='login'),

views.py

@login_required
def account(request):
    return HttpResponseRedirect("https://www.domain.tld/example-redirect/")

def UserLogin(request):
    next_page = request.GET['next']
    if request.user.is_authenticated():
        return HttpResponseRedirect(next_page)
    else:
        if request.method == 'POST':
            if form.is_valid():
                username = form.cleaned_data['username']
                password = form.cleaned_data['password']
                user = authenticate(email=username, password=password)
                if user is not None and user.is_active:
                    login(request, user)
                    return HttpResponseRedirect(next_page)
                else:
                    error_msg = 'There was an error!'
                    return render(request, "login", {'form': form, 'error_msg': error_msg})
            else:
                error_msg = "There was an error!"
                return render(request, "login", {'form':form, 'error_msg':error_msg})
        else:
            form = UserLoginForm()
            return render(request, "login", {'form': form})
Answered By: rwx
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.