1

I am learning Django2,and try to make a login page with csrf_token and ajax.

I hope that if user hasn't lgoin,that will turn to the login page and send a variable next as a tag of the page before login.If user login successfully that I can turn to the homepage or page marked by next.

I read the docs of Django2, and try to code like below,however,when I click "LOGIN" button,it just refresh the login page and get no error

I am confused and have no idea already.Please help.

login views:

def login(request):
    if request.is_ajax():
        uf = UserForm(request.POST)
        if uf.is_valid():
            # get info from form
            username = uf.cleaned_data['username']
            password = uf.cleaned_data['password']
            user = auth.authenticate(request, username=username, password=password)
            if user is not None:  # user match
                auth.login(request, user)
                if request.GET.get('next'):
                    next_url = request.GET.get('next')
                    return JsonResponse({'redirect_url': next_url})
                    # return redirect(request.GET.get('next'))
                else:
                    return JsonResponse({'redirect_url': 'home'})
            else:  # user not match
                error_msg = ["username or pwd mistake"]
                return JsonResponse({'error_msg': error_msg})
    else:
        uf = UserForm()
    return render(request, 'login.html', {'uf': uf})

html :

    <form>
      {% csrf_token %}
       {{ uf.username }}
       {{ uf.password }}
      <div id="errorMsg"></div>
        <button type="submit" class="btn btn-default" id="loginButton">login</button>
     <input type="hidden" name="next" id="redirect-next" value="{{ next|escape }}"/>
   </form>

JQuery:

       $("#loginButton").click(function () {
    $.ajax({
        url: "",
        type: 'POST',
        dataType: "json",
        data: {username: $("#inputEmail3").val(), password: $("#inputPassword3").val()},
        beforeSend: function (xhr, settings) {
            var csrftoken = Cookies.get('csrftoken');
            function csrfSafeMethod(method) {
                // these HTTP methods do not require CSRF protection
                return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
            }
            if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
                xhr.setRequestHeader("X-CSRFToken", csrftoken);
            }
        },
        success: function (result) {
            if (result.error_msg) {
                $('#errorMsg').show().text('user info error') //print an alert on the page
            }
            else {
                location.href = result.redirect_url //turn to homepage or page before login
            }
        }
    })
});
Kamook
  • 49
  • 9
  • Change your type from `submit` to `button`. `submit` type will lead to your form being submitted instead of on click event being triggered. Also check out my answer on a better way to implement the login system in django. – icyfire Jul 03 '18 at 03:15

3 Answers3

2

You don't need to take effort to write a login view of your own like this. Django provides easier methods to implement it.

First make sure the following are included in your settings.py

MIDDLEWARE_CLASSES = [
    ...
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    ...
]
INSTALLED_APPS = [
    ...
    'django.contrib.auth',
    'django.contrib.contenttypes',
    ...
]

Add all the login URLs to your main urls.py:

from django.urls import path
from django.conf.urls import include


urlpatterns = [
....
    path('accounts/', include('django.contrib.auth.urls')),
....
]

Don't forget to run python manage.py migrate to create the tables required for the auth app. Now that the app and URLs are ready, templates need to be created. All the templates for the app should be placed under a folder named registration under your templates directory. The directory structure should be something like.

your_django_app/
    templates/
        registration/
            login.html
    __init__.py
    apps.py
    settings.py
    urls.py
    views.py
    wsgi.py

The contents of the login.html should be something like:

<form id="loginform" action="{% url 'login' %}" method="POST">
{% csrf_token %}
{% if next %}
    <input type="hidden" name="next" value="{{ next }}" />
{% endif %}
    <input name="username" id="id_username" type="text">
    <label>Username</label>
    <input name="password" id="id_password" type="password">
    <label>Password</label>
{% if form.errors %}
    Error! Wrong credentials.
{% endif %}
    <button type="submit">Login</button>
</form>

After this include these in your settings.py file for redirecting users correctly after login.

LOGIN_REDIRECT_URL = '/'
LOGIN_URL = '/accounts/login'

You are all set to go. Make sure to create at least one user before trying this out by running python manage.py createsuperuser. For all pages that require users to login before viewing them you can use the @login_required decorator above their respective view functions to redirect them to the login page before showing the page. Example:

from django.shortcuts import render
from django.contrib.auth.decorators import login_required

@login_required
def home(request):
    return render(request, 'home/index.html')
icyfire
  • 1,170
  • 8
  • 21
  • Actually I have used 'auth' and added @login_required in each page^^ I change the type of button and use HttpResponse(json.dumps.....) instead of JsonResponse(....),it works.It's so kind of you^^thanks – Kamook Jul 03 '18 at 06:58
  • I just mentioned a cleaner method. I thought ajax and jquery were a bit too much for a simple login page. – icyfire Jul 03 '18 at 07:21
  • ajax just for showing some error messages like "username or password error" without refresh the login page. – Kamook Jul 04 '18 at 01:52
2

Here there is a short and simple way to parse the csrf_token using ajax:

Inside the script tag.

    $.ajax({
           url: window.location.pathname,
           type: 'POST',
           data: {
                 ......,
                // Note this step.
                'csrfmiddlewaretoken': "{{ csrf_token }}"
                },
                success: function() {
                   .....
                }
    });

Hope things work well as I have used this thing to parse the csrf_token in my 2 Django based projects. Cheers!

Pujan Mehta
  • 416
  • 5
  • 7
1

This might be related to this issue

As your button tries to submit the form but you want it to be handled handled by the script.

try changing the button type to

type="button" 
paulsbecks
  • 116
  • 1
  • 7