Django user login and registration system

Django user login and registration system

1. create a project

1.1. Create project and app

django-admin startproject mysite_login

python manage.py startapp login

1.2. Set time zone and language

Django uses US time and English by default, in the project's settings file, as shown below:

LANGUAGE_CODE ='en-us'

TIME_ZONE ='UTC'

USE_I18N = True

USE_L10N = True

USE_TZ = True

We changed it to 亚洲/上海time and Chinese

LANGUAGE_CODE ='zh-hans'

TIME_ZONE ='Asia/Shanghai'

USE_I18N = True

USE_L10N = True

USE_TZ = False

1.3. Start

Run and test the project, and visit in the browser of the machinehttp://127.0.0.1:8000/

 2. design the data model

 2.1. Database model design

 As a user login and registration project, all the relevant information of various users needs to be saved. Obviously, we need at least one user table User, and the following information needs to be stored in the user table:

  • username
  • password
  • email address
  • gender
  • Creation time

 Enter login/models.py, the code is as follows

# login/models.py

from django.db import models


class User(models.Model):
    '''user table'''

    gender = (
        ('male','男'),
        ('female','female'),
    )

    name = models.CharField(max_length=128,unique=True)
    password = models.CharField(max_length=256)
    email = models.EmailField(unique=True)
    sex = models.CharField(max_length=32,choices=gender,default='male')
    c_time = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.name

    class Meta:
        ordering = ['c_time']
        verbose_name ='User'
        verbose_name_plural ='User'
    

Meaning of each field:

  • name is required, no more than 128 characters, and unique, that is, there cannot be the same name;
  • Password is required, no more than 256 characters (in fact, it may not need to be so long);
  • Email uses Django's built-in mailbox type and is unique;
  • A choice is used for gender, only male or female can be selected, and the default is male;
  • Use to __str__help humanize the display of object information;
  • In the metadata, users are defined in the reverse order of creation time, that is, the most recent is displayed first;

Note: The user name here refers to the user name registered on the network, and should not be equal to the real name in reality, so a unique mechanism is adopted. If it is a person's name that can be repeated in reality, it must not be set unique.

 2.2. Set the database to Mysql

Modify in settings.py

DATABASES = {
    'default': {
        'ENGINE':'django.db.backends.mysql',
        'NAME':'django', #database name
        'USER':'root', #account
        'PASSWORD': '123456', #Password
        'HOST': '127.0.0.1', #IP
        'PORT': '3306', #Port
    }
}

Import the pymysql module in init.py

# login/init.py

import pymysql
pymysql.install_as_MySQLdb()

  2.3. Database migration

Register the app

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'login',
]

Migrate to database

python manage.py makemigrations

python manage.py migrate

3. admin background

3.1. Register the model in the admin

# login/admin.py

from django.contrib import admin
from. import models

admin.site.register(models.User)

 3.2. Create Super Administrator

python manage.py createsuperuser

Then add a few more test users

 4. url routing and view

 We have created the data model earlier and added some test users in the admin background. Next, we are going to design the URL routing of the site, the corresponding processing view function, and the front-end template used.

 4.1. Routing design

The initial assumption requires the following four URLs:

Considering that the login system belongs to the first-level function of the site, in order to be intuitive and easier to accept, the second-level routing method is not used here, but the routing entry is directly written under the root route, and the reverse resolution name (name parameter) is also not used. .

# mysite_login/urls.py

from django.conf.urls import url
from django.contrib import admin
from login import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^index/', views.index),
    url(r'^login/', views.login),
    url(r'^register/', views.register),
    url(r'^logout/', views.logout),
]

4.2. Preliminary view of the architecture

After the route is written, enter login/views.pythe framework of the file compilation view, the code is as follows:

# login/views.py

from django.shortcuts import render,redirect

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

def login(request):
    pass
    return render(request,'login/login.html')

def register(request):
    pass
    return render(request,'login/register.html')

def logout(request):
    pass
    return redirect('/index/')

We don't rush to complete the specific details inside the view, but build the framework first.

4.3. Create HTML page file

Create a templates directory in the login directory of the project root path, and then create a login directory in the templates directory

login/templates/loginCreate three files in the directory index.html, login.htmland register.html, and write the following code:

index.html

{#login/templates/login/index.html#}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Home</title>
</head>
<body>
<h1>Homepage</h1>
</body>
</html>

login.html

{#login/templates/login/login.html#}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Login</title>
</head>
<body>
<h1>Login page</h1>
</body>
</html>

register.html

{#login/templates/login/register.html#}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Register</title>
</head>
<body>
<h1>Registration page</h1>
</body>
</html>

 5. front-end page design

 5.1. Native HTML page

login.htmlThe contents of the file, write the following code:

{#login/templates/login/login.html#}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Login</title>
</head>
<body>
     <div style="margin: 15% 40%;">
        <h1>Welcome to log in! </h1>
       <form action="/login/" method="post">
            <p>
                <label for="id_username">User name:</label>
                <input type="text" id="id_username" name="username" placeholder="Username" autofocus required/>
            </p>
          
            <p>
                <label for="id_password">Password:</label>
                <input type="password" id="id_password" placeholder="password" name="password" required>
            </p>
            <input type="submit" value="OK">
        </form>
    </div>



</body>
</html>

You can see the page as shown below:

5.2. Introduce Bootstrap

Bootstrap 3.3.7 download address

Create a static directory under the root directory, and bootstrap-3.3.7-distcopy the decompressed directory as a whole to the static directory, as shown in the following figure:

Since Bootstrap relies on JQuery, we need to download and introduce JQuery in advance下载地址

In the static directory, create a new css and js directory as the storage place for future style files and js files, and copy our jquery file to the static/jsdirectory.

Then open the settings file of the project and add configuration at the bottom to specify the search directory for static files:

STATIC_URL ='/static/'
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, "static"),
]

5.3. Create base.html template

Since you want to make the front-end page look like a look, you can't do the same as the previous one, each page has its own writing, and it's alone. A website has its own unified style and common parts, which can be combined into a basic template base.html. Now, create a new base.htmlfile in templates in the root directory to use as the base template for the site.

In the Bootstrap document, we provide us with a very simple and practical basic template, the code is as follows:

<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- The above 3 meta tags *must* be placed first, and any other content *must* follow it! -->
    <title>Bootstrap 101 Template</title>

    <!-- Bootstrap -->
    <link href="css/bootstrap.min.css" rel="stylesheet">

    <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
    <!-- WARNING: Respond.js doesn't work if you view the page via file://-->
    <!--[if lt IE 9]>
      <script src="https://cdn.bootcss.com/html5shiv/3.7.3/html5shiv.min.js"></script>
      <script src="https://cdn.bootcss.com/respond.js/1.4.2/respond.min.js"></script>
    <![endif]-->
  </head>
  <body>
    <h1>Hello, world! </h1>

    <!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
    <script src="https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js"></script>
    <!-- Include all compiled plugins (below), or include individual files as needed -->
    <script src="js/bootstrap.min.js"></script>
  </body>
</html>

Copy it as a whole to the base.htmlfile.

 5.4. Create a page navigation bar

Bootstrap provides ready-made navigation bar components

<nav class="navbar navbar-default">
  <div class="container-fluid">
    <!-- Brand and toggle get grouped for better mobile display -->
    <div class="navbar-header">
      <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
        <span class="sr-only">Toggle navigation</span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
      </button>
      <a class="navbar-brand" href="#">Brand</a>
    </div>

    <!-- Collect the nav links, forms, and other content for toggling -->
    <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
      <ul class="nav navbar-nav">
        <li class="active"><a href="#">Link <span class="sr-only">(current)</span></a></li>
        <li><a href="#">Link</a></li>
        <li class="dropdown">
          <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dropdown <span class="caret"> </span></a>
          <ul class="dropdown-menu">
            <li><a href="#">Action</a></li>
            <li><a href="#">Another action</a></li>
            <li><a href="#">Something else here</a></li>
            <li role="separator" class="divider"></li>
            <li><a href="#">Separated link</a></li>
            <li role="separator" class="divider"></li>
            <li><a href="#">One ​​more separated link</a></li>
          </ul>
        </li>
      </ul>
      <form class="navbar-form navbar-left">
        <div class="form-group">
          <input type="text" class="form-control" placeholder="Search">
        </div>
        <button type="submit" class="btn btn-default">Submit</button>
      </form>
      <ul class="nav navbar-nav navbar-right">
        <li><a href="#">Link</a></li>
        <li class="dropdown">
          <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dropdown <span class="caret"> </span></a>
          <ul class="dropdown-menu">
            <li><a href="#">Action</a></li>
            <li><a href="#">Another action</a></li>
            <li><a href="#">Something else here</a></li>
            <li role="separator" class="divider"></li>
            <li><a href="#">Separated link</a></li>
          </ul>
        </li>
      </ul>
    </div><!--/.navbar-collapse -->
  </div><!--/.container-fluid -->
</nav>

There are some parts, such as the search box, which we don’t need yet, and the extra content needs to be cut out. At the same time, some names and url addresses need to be modified according to our actual content. The code of the final navigation bar is as follows:

<nav class="navbar navbar-default">
      <div class="container-fluid">
        <!-- Brand and toggle get grouped for better mobile display -->
        <div class="navbar-header">
          <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#my-nav" aria-expanded="false">
            <span class="sr-only">Toggle navigation bar</span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
          </button>
          <a class="navbar-brand" href="#">Mysite</a>
        </div>

        <!-- Collect the nav links, forms, and other content for toggling -->
        <div class="collapse navbar-collapse" id="my-nav">
          <ul class="nav navbar-nav">
            <li class="active"><a href="/index/">Homepage</a></li>
          </ul>
          <ul class="nav navbar-nav navbar-right">
            <li><a href="/login/">Login</a></li>
            <li><a href="/register/">Register</a></li>
          </ul>
        </div><!--/.navbar-collapse -->
      </div><!--/.container-fluid -->
    </nav>

5.5. Use Bootstrap static files

{% static '相对路径' %}This static file loading method provided by Django for us can link pages with static files

 Finally, the base.htmlcontent is as follows:

{% load staticfiles %}

<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- The above 3 meta tags *must* be placed first, and any other content *must* follow it! -->
    <title>{% block title %}base{% endblock %}</title>

    <!-- Bootstrap -->
    <link href="{% static'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}" rel="stylesheet">

    <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
    <!-- WARNING: Respond.js doesn't work if you view the page via file://-->
    <!--[if lt IE 9]>
      <script src="https://cdn.bootcss.com/html5shiv/3.7.3/html5shiv.min.js"></script>
      <script src="https://cdn.bootcss.com/respond.js/1.4.2/respond.min.js"></script>
    <![endif]-->
    {% block css %}{% endblock %}
  </head>
  <body>
    <nav class="navbar navbar-default">
      <div class="container-fluid">
        <!-- Brand and toggle get grouped for better mobile display -->
        <div class="navbar-header">
          <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#my-nav" aria-expanded="false">
            <span class="sr-only">Toggle navigation bar</span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
          </button>
          <a class="navbar-brand" href="#">Mysite</a>
        </div>

        <!-- Collect the nav links, forms, and other content for toggling -->
        <div class="collapse navbar-collapse" id="my-nav">
          <ul class="nav navbar-nav">
            <li class="active"><a href="/index/">Homepage</a></li>
          </ul>
          <ul class="nav navbar-nav navbar-right">
            <li><a href="/login/">Login</a></li>
            <li><a href="/register/">Register</a></li>
          </ul>
        </div><!--/.navbar-collapse -->
      </div><!--/.container-fluid -->
    </nav>

    {% block content %}{% endblock %}


    <!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
    <script src="{% static'js/jquery-3.2.1.js' %}"></script>
    <!-- Include all compiled plugins (below), or include individual files as needed -->
    <script src="{% static'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}"></script>
  </body>
</html>

brief introduction:

  • The {% load staticfiles %}static method can be used only after loading at the top of the page ;
  • Pass {% block title %}base{% endblock %}, set up a dynamic page title block;
  • Pass {% block css %}{% endblock %}, set up a dynamic css loading block;
  • Passed {% block content %}{% endblock %}, leaving an interface for the main content of the specific page;
  • By {% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}pointing the style file to our actual static file, the following js script is the same.

 See the effect

5.6. Design the login page

Bootstarp provides a basic form style, the code is as follows:

<form>
  <div class="form-group">
    <label for="exampleInputEmail1">Email address</label>
    <input type="email" class="form-control" id="exampleInputEmail1" placeholder="Email">
  </div>
  <div class="form-group">
    <label for="exampleInputPassword1">Password</label>
    <input type="password" class="form-control" id="exampleInputPassword1" placeholder="Password">
  </div>
  <div class="form-group">
    <label for="exampleInputFile">File input</label>
    <input type="file" id="exampleInputFile">
    <p class="help-block">Example block-level help text here.</p>
  </div>
  <div class="checkbox">
    <label>
      <input type="checkbox"> Check me out
    </label>
  </div>
  <button type="submit" class="btn btn-default">Submit</button>
</form>

as follows:

We combined Bootstrap and the form we wrote earlier and modified it login/templates/login/login.htmlto meet the requirements of the project:

{% extends'login/base.html' %}
{% load staticfiles %}
{% block title %}Login{% endblock %}
{% block css %}
    <link rel="stylesheet" href="{% static'css/login.css' %}">
{% endblock %}


{% block content %}
    <div class="container">
        <div class="col-md-4 col-md-offset-4">
          <form class='form-login' action="/login/" method="post">
              <h2 class="text-center">Welcome to login</h2>
              <div class="form-group">
                <label for="id_username">User name:</label>
                <input type="text" name='username' class="form-control" id="id_username" placeholder="Username" autofocus required>
              </div>
              <div class="form-group">
                <label for="id_password">Password:</label>
                <input type="password" name='password' class="form-control" id="id_password" placeholder="Password" required>
              </div>
              <button type="reset" class="btn btn-default pull-left">Reset</button>
              <button type="submit" class="btn btn-primary pull-right">Submit</button>
          </form>
        </div>
    </div> <!--/container -->
{% endblock %}

Description:

  • By {% extends 'base.html' %}inheriting the content of the'base.html' template;
  • By {% block title %}登录{% endblock %}setting a special title;
  • Through the block cssintroduction of targeted login.cssstyle files;
  • The main content is defined block contentinternally
  • A reset button has been added.

static/cssCreate a new login.cssstyle file in the directory , here simply write a little style,

body {
  background-color: #eee;
}
.form-login {
  max-width: 330px;
  padding: 15px;
  margin: 0 auto;
}
.form-login .form-control {
  position: relative;
  height: auto;
  -webkit-box-sizing: border-box;
     -moz-box-sizing: border-box;
          box-sizing: border-box;
  padding: 10px;
  font-size: 16px;
}
.form-login .form-control:focus {
  z-index: 2;
}
.form-login input[type="text"] {
  margin-bottom: -1px;
  border-bottom-right-radius: 0;
  border-bottom-left-radius: 0;
}
.form-login input[type="password"] {
  margin-bottom: 10px;
  border-top-left-radius: 0;
  border-top-right-radius: 0;
}

Final effect

 6. login view

6.1. Login view

According to our design in routing, the user login.htmlfills in the username and password through the form in and sends it to the server's /login/address by POST . The server receives and processes this request through login/views.pythe login()view function in.

We can receive and process requests in the following ways:

def login(request):
    if request.method == "POST":
        username = request.POST.get('username')
        password = request.POST.get('password') return redirect('/index/')
    return render(request,'login/login.html')

You also need to add a {% csrf_token %}label in the form of the front page :

<form class='form-login' action="/login/" method="post">
  {% csrf_token %}
  <h2 class="text-center">Welcome to login</h2>
  <div class="form-group">
  ......
</form>

Enter the login page, enter the user name, password and then jump to the index page.

6.2. Data verification

Use Django's ORM to query user data in the database through a unique user name. If there is a match, a password comparison is performed. If there is no match, the user name does not exist. If the password comparison is wrong, the password is incorrect.

def login(request):
    if request.method == "POST":
        username = request.POST.get('username', None)
        password = request.POST.get('password', None)
        if username and password: # Make sure that the username and password are not empty
            username = username.strip()
            # User name conforms to legal verification
            # Password length verification
            # More other verifications.....
            try:
                user = models.User.objects.get(name=username)
            except:
                return render(request,'login/login.html')
            if user.password == password:
                return redirect('/index/')
    return render(request,'login/login.html')

6.3. Add prompt information

The above code still lacks a very important part of the content, prompt information! Regardless of whether the login was successful or failed, the user did not get any prompt information, which is obviously not acceptable.

Modify the login view:

def login(request):
    if request.method == "POST":
        username = request.POST.get('username', None)
        password = request.POST.get('password', None)
        message = "All fields must be filled in!"
        if username and password: # Make sure that the username and password are not empty
            username = username.strip()
            # User name conforms to legal verification
            # Password length verification
            # More other verifications.....
            try:
                user = models.User.objects.get(name=username)
                if user.password == password:
                    return redirect('/index/')
                else:
                    message = "The password is incorrect!"
            except:
                message = "Username does not exist!"
        return render(request,'login/login.html', {"message": message})
    return render(request,'login/login.html')

The message variable is added to save the prompt information. When there is an error message, pack the error message into a dictionary, and then provide it to the render() method as the third parameter. This data dictionary will be passed to the template for you to call when the template is rendered.

In order to display information on the front-end page, you also need to login.htmlmodify:

{% extends'login/base.html' %}
{% load staticfiles %}
{% block title %}Login{% endblock %}
{% block css %}
    <link rel="stylesheet" href="{% static'css/login.css' %}">
{% endblock %}


{% block content %}
    <div class="container">
        <div class="col-md-4 col-md-offset-4">
            <form class='form-login' action="/login/" method="post">

                {% if message %}
                    <div class="alert alert-warning">{{ message }}</div>
                {% endif %}

                {% csrf_token %}
                <h2 class="text-center">Welcome to login</h2>
                <div class="form-group">
                    <label for="id_username">User name:</label>
                    <input type="text" name='username' class="form-control" id="id_username" placeholder="Username"
                           autofocus required>
                </div>
                <div class="form-group">
                    <label for="id_password">Password:</label>
                    <input type="password" name='password' class="form-control" id="id_password" placeholder="Password"
                           required>
                </div>
                <button type="reset" class="btn btn-default pull-left">Reset</button>
                <button type="submit" class="btn btn-primary pull-right">Submit</button>
            </form>
        </div>
    </div> <!--/container -->
{% endblock %}

index.htmlModify the homepage template, delete the original content, and add the following code:

{#login/templates/login/index.html#}

{% extends'login/base.html' %}
{% block title %}Home page{% endblock %}
{% block content %}
    <h1>Welcome back! </h1>
{% endblock %}

 7. Django form

Django forms provide us with the following three main functions:

  • Prepare and reconstruct data for page rendering;
  • Create HTML form elements for the data;
  • Receive and process the data sent by the user from the form

 7.1. Create a form model

from django import forms


class UserForm(forms.Form):
    username = forms.CharField(label="Username", max_length=128)
    password = forms.CharField(label="Password", max_length=256, widget=forms.PasswordInput)

Description:

  • Import the forms module first
  • All form classes must inherit the forms.Form class
  • Each form field has its own field type such as CharField, which corresponds to <form>an input element in an HTML language . This is very similar to the design of the Django model system.
  • The label parameter is used to set the <label>label
  • max_lengthLimit the maximum length of the field input. It plays two roles at the same time. One is to limit the number of characters input by the user on the browser page, and the other is to verify the length of the user input on the back-end server.
  • widget=forms.PasswordInputUsed to specify that the field appears in the form <input type='password'/>, that is, the password input box.

 7.2. Modify the view

After using the Django form, you must make the corresponding changes in the view:

# login/views.py

from django.shortcuts import render,redirect
from. import models
from .forms import UserForm

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

def login(request):
    if request.method == "POST":
        login_form = UserForm(request.POST)
        message = "Please check the content!"
        if login_form.is_valid():
            username = login_form.cleaned_data['username']
            password = login_form.cleaned_data['password']
            try:
                user = models.User.objects.get(name=username)
                if user.password == password:
                    return redirect('/index/')
                else:
                    message = "The password is incorrect!"
            except:
                message = "The user does not exist!"
        return render(request,'login/login.html', locals())

    login_form = UserForm()
    return render(request,'login/login.html', locals())

Description:

  • For non-POST method to send data, such as GET method request page, return an empty form, so that the user can fill in the data;
  • For the POST method, receive form data and verify;
  • Use the is_valid()method that comes with the form class to complete the data verification in one step;
  • After successful verification, cleaned_datathe specific value of the form can be obtained from the data dictionary of the form object ;
  • If the verification fails, a form containing the previous data is returned to the front-end page for the user to modify. In other words, it will help you keep the previously filled data content instead of returning an empty form!

In addition, a little trick is used here. Python has a built-in locals() function, which returns all current local variable dictionaries. We can lazily use this as the data dictionary parameter value of the render function, so we don’t have to bother to construct a shape like {'message':message, 'login_form':login_form}Dictionary too. The advantage of this is of course that it greatly facilitates us, but at the same time it may also pass some redundant variable data to the template, causing data redundancy and reducing efficiency.

7.3. Modify the login interface

A very important function of Django's forms is to automatically generate HTML form content. Now, we need to modify the original login.htmlfile:

{% extends'base.html' %}
{% load staticfiles %}
{% block title %}Login{% endblock %}
{% block css %}<link href="{% static'css/login.css' %}" rel="stylesheet"/>{% endblock %}


{% block content %}
    <div class="container">
        <div class="col-md-4 col-md-offset-4">
          <form class='form-login' action="/login/" method="post">

              {% if message %}
                  <div class="alert alert-warning">{{ message }}</div>
              {% endif %}
              {% csrf_token %}
              <h2 class="text-center">Welcome to login</h2>

              {{ login_form }}

              <button type="reset" class="btn btn-default pull-left">Reset</button>
              <button type="submit" class="btn btn-primary pull-right">Submit</button>

          </form>
        </div>
    </div> <!--/container -->
{% endblock %}

HTML source code generated by the browser

Restart the server and refresh the page, as shown in the figure below:

7.4. Manually render the form

Direct {{ login_form }}is good enough, and consequently do not have to worry about, but the interface is really ugly, often not what you want, if you want to use CSS and JS, for example you want to introduce Bootstarps framework, these need to input elements in the form of additional Control, what should I do? Render the fields manually.

You can {{ login_form.name_of_field }}get each field and render it separately, as shown in the following example:

<div class="form-group">
  {{ login_form.username.label_tag }}
  {{ login_form.username}}
</div>
<div class="form-group">
  {{ login_form.password.label_tag }}
  {{ login_form.password }}
</div>

Then, add the attr attribute to the form class, and modify it as shown belowlogin/forms.py

from django import forms

class UserForm(forms.Form):
    username = forms.CharField(label="Username", max_length=128, widget=forms.TextInput(attrs={'class':'form-control'}))
    password = forms.CharField(label="Password", max_length=256, widget=forms.PasswordInput(attrs={'class':'form-control'}))

Refresh the page again and it will display normally!

 8. Picture verification code

In order to prevent bots from frequently logging in to websites or malicious logins by saboteurs, many user login and registration systems provide graphical verification code functions.

CAPTCHA is the abbreviation of "Completely Automated Public Turing test to tell Computers and Humans Apart", which is a public fully automatic program that distinguishes whether a user is a computer or a human. It can prevent malicious cracking of passwords, swiping tickets, and forum watering, effectively preventing a hacker from using a specific program to brute force a specific registered user to make continuous login attempts.

The graphic verification code has a long history, and it is a bit of a heroic end. Because of the existence of machine learning and image recognition, robots can already recognize characters in images more correctly. But in any case, as a defensive method, it can at least resist some low-level entry-level attack methods, raising the threshold of attackers.

It is very simple to implement the image verification code function in Django. There are ready-made third-party libraries to use, and we don't need to develop it ourselves (and we must be able to develop it, 囧). This library is called django-simple-captcha.

8.1. Install captcha

Direct installation: pip install django-simple-captcha

Django automatically helped us install related dependent libraries six, olefileand Pillow, among which Pillow is the famous drawing module.

Register captcha

In settings, register'captcha' to the app list:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'login',
    'captcha',
]

Captcha needs to create its own data table in the database, so you need to execute the migrate command to generate the data table:

python manage.py migrate

8.2. Add URL routing

Add the URL corresponding to captcha in the urls.py file in the root directory:

from django.conf.urls import url
from django.conf.urls import include
from django.contrib import admin
from login import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^index/', views.index),
    url(r'^login/', views.login),
    url(r'^register/', views.register),
    url(r'^logout/', views.logout),
    url(r'^captcha', include('captcha.urls')) # add this line
]

8.3. Modify forms.py

If the above is OK, you can directly add CaptchaField to our forms.py file.

from django import forms
from captcha.fields import CaptchaField

class UserForm(forms.Form):
    username = forms.CharField(label="Username", max_length=128, widget=forms.TextInput(attrs={'class':'form-control'}))
    password = forms.CharField(label="Password", max_length=256, widget=forms.PasswordInput(attrs={'class':'form-control'}))
    captcha = CaptchaField(label='Verification Code')

You need to import it in advance from captcha.fields import CaptchaField, and then add a captcha field just like writing a normal form field!

 8.4. Modify login.html

 Since we are manually generated form form, we need to modify it and add the relevant content of captcha, as shown below:

{% extends'login/base.html' %}
{% load staticfiles %}
{% block title %}Login{% endblock %}
{% block css %}
    <link rel="stylesheet" href="{% static'css/login.css' %}">
{% endblock %}


{% block content %}
    <div class="container">
        <div class="col-md-4 col-md-offset-4">
          <form class='form-login' action="/login/" method="post">

              {% if message %}
                  <div class="alert alert-warning">{{ message }}</div>
              {% endif %}
              {% csrf_token %}
              <h2 class="text-center">Welcome to login</h2>
              <div class="form-group">
                  {{ login_form.username.label_tag }}
                  {{ login_form.username}}
              </div>
              <div class="form-group">
                  {{ login_form.password.label_tag }}
                  {{ login_form.password }}
              </div>

              <div class="form-group">
                  {{ login_form.captcha.errors }}
                  {{ login_form.captcha.label_tag }}
                  {{ login_form.captcha }}
              </div>

              <button type="reset" class="btn btn-default pull-left">Reset</button>
              <button type="submit" class="btn btn-primary pull-right">Submit</button>

          </form>
        </div>
    </div> <!--/container -->
{% endblock %}

An extra one is added here {{ login_form.captcha.errors }}to clearly indicate to the user that your verification code is incorrect

Check the effect:

The work of verifying whether the graphic code is correct is done automatically in the background. You only need to use is_valid()the built-in verification method of this forms to proceed together. There is no need to add any verification code to the view function at all, which is very convenient and fast!

 9. session session

        Because of the characteristics of the Internet HTTP protocol, every request from the user's browser is stateless and independent. In layman's terms, the user state cannot be saved, and the backend server does not know whether the current request and the previous and future requests are from the same user. For static websites, this may not be a problem, while for dynamic websites, especially shopping or financial websites such as JD.com, Tmall, and banks, it is fatal to fail to identify users and maintain user status, and they cannot provide services at all. You can try to turn off the cookie function of your browser, and you will find that you will not be able to log in and shop on JD.com.

In order to achieve the function of maintaining the connection status, the website will write some data in the limited hard disk location in the user's machine through the user's browser, which is the so-called cookie. Cookies can save various data such as user name, browsing history, form history, login and logout. But this method is very insecure, because the Cookie is stored on the user’s machine. If the Cookie is forged, tampered with, or deleted, it will cause a great security threat. Therefore, modern website design usually uses Cookie to store some unimportant things. The content, actual user data and state are still stored on the server side in the form of Session sessions.

Session depends on Cookie! But the difference from Cookie is that Session puts all data on the server side, and only a non-plain text identifying information, such as hash value, is stored in the cookie of the user's browser.

Django provides a general Session framework, and can use a variety of ways to save session data:

  • Save in the database
  • Save to cache
  • Save to file
  • Save in cookie

Generally, if there is no special requirement, please use the method of saving in the database, and try not to save it in the Cookie.

Django's session framework is enabled by default and has been registered in the app settings. If it is really not enabled, then refer to the following content to add the two lines with instructions, and then execute the migrate command to create a data table, and you can use session.

# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions', # this line
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

MIDDLEWARE = ​​[
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware', # this line
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

When the session is enabled, the HttpRequest object passed to the view request parameter will contain a session attribute, just like a dictionary object. You can read and write request.sessionattributes anywhere in Django , or edit and use it multiple times.

The following is a reference for session usage:

class backends.base.SessionBase
        # This is the base class of all session objects, including standard dictionary methods:
        __getitem__(key)
            Example: fav_color = request.session['fav_color']
        __setitem__(key, value)
            Example: request.session['fav_color'] ='blue'
        __delitem__(key)
            Example: del request.session['fav_color'] # If it does not exist, an exception will be thrown
        __contains__(key)
            Example:'fav_color' in request.session
        get(key, default=None)
            Example: fav_color = request.session.get('fav_color','red')
        pop(key, default=__not_given)
            Example: fav_color = request.session.pop('fav_color','blue')
 # Built-in method similar to dictionary data type
        keys()
        items()
        setdefault()
        clear()


        # It also has the following methods:
        flush()
            # Delete the current session data and session cookie. It is often used to delete the session after the user logs out.

        set_test_cookie()
            # Set a test cookie to detect whether the user's browser supports cookies. Due to the working mechanism of cookies, you can only test when the user requests it next time.
        test_cookie_worked()
            # Return True or False, depending on whether the user's browser accepts the test cookie. You must call the set_test_cookie() method first.
        delete_test_cookie()
            # Delete the test cookie.
        set_expiry(value)
            # Set the validity period of the cookie. Different types of parameter values ​​can be passed:
        • If the value is an integer, the session will expire after the corresponding number of seconds. For example, request.session.set_expiry(300) will expire after 300 seconds.
        • If the value is a datetime or timedelta object, the session will expire on the specified date
        • If it is 0, it will be invalid after the user closes the browser
        • If it is None, the global session invalidation policy will be used
        The expiration time starts from the moment when the last session was modified.

        get_expiry_age()
            # Return the number of seconds that expire after how many seconds. For sessions without a custom expiration time, this is equivalent to SESSION_COOKIE_AGE.
            # This method accepts 2 optional keyword parameters
        • modification: the last modification time of the session (datetime object). The default is the current time.
        •Expiry: session invalidation information, which can be a datetime object, or int or None

        get_expiry_date()
            # Similar to the above method, but returns the date

        get_expire_at_browser_close()
            # Return True or False, depending on whether the user session ends after the browser is closed.

        clear_expired()
            # Delete the invalid session data.
        cycle_key()
            # Create a new session key to keep the current session data. django.contrib.auth.login() will call this method.

9.1. Use session

1. modify login/views.pythe login() view function:

def login(request):
    if request.session.get('is_login',None):
        return redirect('/index')

    if request.method == "POST":
        login_form = UserForm(request.POST)
        message = "Please check the content!"
        if login_form.is_valid():
            username = login_form.cleaned_data['username']
            password = login_form.cleaned_data['password']
            try:
                user = models.User.objects.get(name=username)
                if user.password == password:
                    request.session['is_login'] = True
                    request.session['user_id'] = user.id
                    request.session['user_name'] = user.name
                    return redirect('/index/')
                else:
                    message = "The password is incorrect!"
            except:
                message = "The user does not exist!"
        return render(request,'login/login.html', locals())

    login_form = UserForm()
    return render(request,'login/login.html', locals())

Through the following if statement, we do not allow repeated logins:

if request.session.get('is_login',None):
    return redirect("/index/")

Through the following statement, we write user status and data to the session dictionary:

request.session['is_login'] = True
request.session['user_id'] = user.id
request.session['user_name'] = user.name

You can write any data in it, not just user-related!

Now that we have a session to record the user's login status, we can improve our logout view function:

def logout(request):
    if not request.session.get('is_login', None):
        # If you have not logged in, you will not log out
        return redirect("/index/")
    request.session.flush()
    # Or use the following method
    # del request.session['is_login']
    # del request.session['user_id']
    # del request.session['user_name']
    return redirect("/index/")

The flush() method is a safer method, and all the contents in the session are emptied at one time to ensure that no future troubles are left. But there is also a downside, that is, if you carry a bit of "private goods" in the session, it will be deleted altogether. You must pay attention to this.

 9.2. Improve the page

With the user status, you can display different pages according to whether the user is logged in or not, such as the content of the navigation bar:

1. modify the base.htmlfile:

 <div class="collapse navbar-collapse" id="my-nav">
          <ul class="nav navbar-nav">
            <li class="active"><a href="/index/">Homepage</a></li>
          </ul>
          <ul class="nav navbar-nav navbar-right">
              {% if request.session.is_login %}
                  <li><a href="#">Currently online: {{ request.session.user_name }}</a></li>
                  <li><a href="/logout/">Logout</a></li>
              {% else %}
                  <li><a href="/login/">Login</a></li>
                  <li><a href="/register/">Register</a></li>
              {% endif %}
          </ul>
        </div><!--/.navbar-collapse -->
      </div><!--/.container-fluid -->

Judging by if, when logging in, the current user name and logout button are displayed. When not logged in, the login and registration buttons are displayed.

Pay attention to the template language, {{ request }}this variable will be passed into the template by default, you can get its internal {{ request.session }}content through the dot call method, and then further get the content in the session. In fact {{ request }}, the data in is far more than that, for example, {{ request.path }}you can get the previous URL address.

Modify the index.htmlpage again to display different content according to whether you are logged in or not:

{% extends'base.html' %}
{% block title %}Home page{% endblock %}
{% block content %}
    {% if request.session.is_login %}
    <h1>Hello, {{ request.session.user_name }}! Welcome back! </h1>
    {% else %}
    <h1>You are not logged in, you can only access public content! </h1>
    {% endif %}
{% endblock %}

Look at the effect:

 10. Registration view

 10.1. Create forms

In /login/forms.pyadding a new form class:

class RegisterForm(forms.Form):
    gender = (
        ('male', "男"),
        ('female', "女"),
    )
    username = forms.CharField(label="Username", max_length=128, widget=forms.TextInput(attrs={'class':'form-control'}))
    password1 = forms.CharField(label="Password", max_length=256, widget=forms.PasswordInput(attrs={'class':'form-control'}))
    password2 = forms.CharField(label="Confirm password", max_length=256, widget=forms.PasswordInput(attrs={'class':'form-control'}))
    email = forms.EmailField(label="Email address", widget=forms.EmailInput(attrs={'class':'form-control'}))
    sex = forms.ChoiceField(label='gender', choices=gender)
    captcha = CaptchaField(label='Verification Code')

Description:

  • Gender is the same as in the User model. In fact, it can be pulled out and shared as a constant. In order to be intuitive, it is rewritten specially;
  • password1 and password2 are used to enter the password twice and compare them to prevent entering the password by mistake;
  • email is a mailbox input box;
  • sex is a select drop-down box;

 10.2. Improve register.html

Similarly, similar to the login.html file, we write related entries in the form in register.html:

{% extends'login/base.html' %}

{% block title %}Sign up{% endblock %}
{% block content %}
    <div class="container">
        <div class="col-md-4 col-md-offset-4">
          <form class='form-register' action="/register/" method="post">

              {% if message %}
                  <div class="alert alert-warning">{{ message }}</div>
              {% endif %}

              {% csrf_token %}

              <h2 class="text-center">Welcome to register</h2>
              <div class="form-group">
                  {{ register_form.username.label_tag }}
                  {{ register_form.username}}
              </div>
              <div class="form-group">
                  {{ register_form.password1.label_tag }}
                  {{ register_form.password1 }}
              </div>
              <div class="form-group">
                  {{ register_form.password2.label_tag }}
                  {{ register_form.password2 }}
              </div>
              <div class="form-group">
                  {{ register_form.email.label_tag }}
                  {{ register_form.email }}
              </div>
              <div class="form-group">
                  {{ register_form.sex.label_tag }}
                  {{ register_form.sex }}
              </div>
              <div class="form-group">
                  {{ register_form.captcha.errors }}
                  {{ register_form.captcha.label_tag }}
                  {{ register_form.captcha }}
              </div>

              <button type="reset" class="btn btn-default pull-left">Reset</button>
              <button type="submit" class="btn btn-primary pull-right">Submit</button>

          </form>
        </div>
    </div> <!--/container -->
{% endblock %}

10.3. Registration View

Enter the /login/views.pyfile, now to improve our register()view:

def register(request):
    if request.session.get('is_login', None):
        # Login status does not allow registration. You can modify this principle!
        return redirect("/index/")
    if request.method == "POST":
        register_form = RegisterForm(request.POST)
        message = "Please check the content!"
        if register_form.is_valid(): # Get data
            username = register_form.cleaned_data['username']
            password1 = register_form.cleaned_data['password1']
            password2 = register_form.cleaned_data['password2']
            email = register_form.cleaned_data['email']
            sex = register_form.cleaned_data['sex']
            if password1 != password2: # Determine whether the two passwords are the same
                message = "The two passwords entered are different!"
                return render(request,'login/register.html', locals())
            else:
                same_name_user = models.User.objects.filter(name=username)
                if same_name_user: # The user name is unique
                    message ='The user already exists, please select a user name again! '
                    return render(request,'login/register.html', locals())
                same_email_user = models.User.objects.filter(email=email)
                if same_email_user: # Unique email address
                    message ='This email address has been registered, please use another email address! '
                    return render(request,'login/register.html', locals())

                # When everything is OK, create a new user

                new_user = models.User.objects.create()
                new_user.name = username
                new_user.password = password1
                new_user.email = email
                new_user.sex = sex
                new_user.save()
                return redirect('/login/') # Automatically jump to the login page
    register_form = RegisterForm()
    return render(request,'login/register.html', locals())

From the general logic to instantiate an object is a RegisterForm then use is_valide()the verification data, and then from the cleaned_dataacquired data.

The key point is the registration logic. 1. the password entered twice must be the same, and secondly, the same user name and mailbox cannot exist. Finally, if the conditions are met, use the ORM API to create a user instance, and then save it in the database.

Take a look at the registered page:

Registered successfully, you can see the registered users in the admin background

10.4. Password encryption

The password for user registration should be encrypted

There are many different ways to encrypt a password, and the degree of security varies. Here we use Python's built-in hashlib library to encrypt the password using hash values. The security level may not be high enough, but it is simple enough and convenient to use, isn't it?

First login/views.pywrite a hash function in:

import hashlib

def hash_code(s, salt='mysite'):# add some salt
    h = hashlib.sha256()
    s += salt
    h.update(s.encode()) # The update method only accepts bytes type
    return h.hexdigest()

Then, we have to modify the login() and register() views:

#login.html

if user.password == hash_code(password): # Compare the hash value with the value in the database

#register.html

new_user.password = hash_code(password1) # Use encrypted password
# login/views.py

from django.shortcuts import render,redirect
from. import models
from .forms import UserForm,RegisterForm
import hashlib

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

def login(request):
    if request.session.get('is_login', None):
        return redirect("/index/")
    if request.method == "POST":
        login_form = UserForm(request.POST)
        message = "Please check the content!"
        if login_form.is_valid():
            username = login_form.cleaned_data['username']
            password = login_form.cleaned_data['password']
            try:
                user = models.User.objects.get(name=username)
                if user.password == hash_code(password): # Compare the hash value with the value in the database
                    request.session['is_login'] = True
                    request.session['user_id'] = user.id
                    request.session['user_name'] = user.name
                    return redirect('/index/')
                else:
                    message = "The password is incorrect!"
            except:
                message = "The user does not exist!"
        return render(request,'login/login.html', locals())

    login_form = UserForm()
    return render(request,'login/login.html', locals())


def register(request):
    if request.session.get('is_login', None):
        # Login status does not allow registration. You can modify this principle!
        return redirect("/index/")
    if request.method == "POST":
        register_form = RegisterForm(request.POST)
        message = "Please check the content!"
        if register_form.is_valid(): # Get data
            username = register_form.cleaned_data['username']
            password1 = register_form.cleaned_data['password1']
            password2 = register_form.cleaned_data['password2']
            email = register_form.cleaned_data['email']
            sex = register_form.cleaned_data['sex']
            if password1 != password2: # Determine whether the two passwords are the same
                message = "The two passwords entered are different!"
                return render(request,'login/register.html', locals())
            else:
                same_name_user = models.User.objects.filter(name=username)
                if same_name_user: # The user name is unique
                    message ='The user already exists, please select a user name again! '
                    return render(request,'login/register.html', locals())
                same_email_user = models.User.objects.filter(email=email)
                if same_email_user: # Unique email address
                    message ='This email address has been registered, please use another email address! '
                    return render(request,'login/register.html', locals())

                # When everything is OK, create a new user

                new_user = models.User.objects.create()
                new_user.name = username
                new_user.password = hash_code(password1) # Use encrypted password
                new_user.email = email
                new_user.sex = sex
                new_user.save()
                return redirect('/login/') # Automatically jump to the login page
    register_form = RegisterForm()
    return render(request,'login/register.html', locals())

def logout(request):
    if not request.session.get('is_login',None):
        return redirect('/index/')
    request.session.flush()

    return redirect('/index/')

def hash_code(s, salt='mysite_login'):
    h = hashlib.sha256()
    s += salt
    h.update(s.encode()) # The update method only accepts bytes type
    return h.hexdigest()

Restart the server, enter the registration page, create a new user, and then enter the admin background to check the user's password:

 Log in with that user again, and you're done!

You can see that the password length has become very long depending on your hash algorithm, so when setting the password field in the previous model, don't take it for granted to max_lengthset it to a number as small as 16.

Reference: https://cloud.tencent.com/developer/article/1091547 Django user login and registration system-Cloud + Community-Tencent Cloud