This article is participating in “Python Theme Month”. Check out Python Authoring season to show off your Python articles – $2000 limited prize waiting for you to win

Gitee address 👉 saas This tutorial corresponds to the code for [registration verification code processing] submission, can be viewed through the corresponding branch

User Registration

First, the overall mind map is as follows:

1. Prepare

1.1 Tencent Cloud sends SMS

  • The login and registration function of the project needs to use the mobile phone number to receive SMS messages. Tencent cloud SMS is used here
  • Tencent Cloud short Message Service (SMS) using Python

1.2 redis

  • Redis can be downloaded, installed and operated by Python, and django can be connected to Redis

2. Display the registration page

2.1 create app

  • Create a file namedwebThe following code is written in this app
    python manage.py startapp web
    Copy the code

2.2 the app to register

  • settings.pyRegister app in file,INSTALLED_APPSAdd the app you just created
    INSTALLED_APPS = [
        'django.contrib.admin'.'django.contrib.auth'.'django.contrib.contenttypes'.'django.contrib.sessions'.'django.contrib.messages'.'django.contrib.staticfiles'.'app01.apps.App01Config'.'web.apps.WebConfig',]Copy the code

2.3 Master preparation

2.3.1 Plug-in introduction

  • Before creating the master version, we need to introduce plug-ins such as Bootstrap and JS, which can be used by CDN or downloaded for offline use. Here we download them and place them in thestaticFolder, so we can use it later

Can go to their website to download the offline files, can also take I have downloaded good here, I put it in the cloud, the need for download, including: js, the bootstrap, the font – awesome links: icon 】 【 pan.baidu.com/s/1gQRN57Xg…

Extraction code: MNJL Decompression password: ruochen666

  • inwebNext create a static file storestaticFolder and then create another onepluginThe bootstrap folder is used to store the tool class files, and then place the downloaded JS, bootstrap, and font-awesome into thestaticIn the folder, the structure is shown as follows

  • And then we can introduce it directly when we’re going to use itstaticFiles under the folder

2.3.2 stamper

Why use master? In the front-end page, the registration and login pages are basically similar, so we can make these two pages inherit from the master version to achieve code reuse

  • inwebSo let’s create onetemplatesFolder, intemplatesCreate another one under the folderlayoutFolder for our master filesbasic.html
    • Structure is as follows

- 'basic.html' code is as follows, The style of the navigation bar directly from [the bootstrap website components] over here (https://v3.bootcss.com/components/#navbar) can be modified ` ` ` HTML {% load static %} <! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>{% block title %}{% endblock %}</title> <link rel="stylesheet" href="{% static '/plugin/bootstrap/css/bootstrap.min.css' %}"> <link rel="stylesheet" href="{% static '/plugin/font-awesome/css/font-awesome.min.css' %}"> <style> .navbar-default{ border-radius: 0; } </style> {% block css %}{% endblock %} </head> <body> <nav class="navbar navbar-default"> <div class="container"> <! -- 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="#">Tracer</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 > < a href = "#" > product features < / a > < / li > < li > < a href = "#" > enterprise solutions < / a > < / li > < li > < a href = "#" > help document < / a > < / li > < li > < a Price href = "#" > < / a > < / li > < / ul > < ul class = "nav navbar - nav navbar - right" > < li > < a href = "#" > Link < / a > < / li > < li class = "dropdown can" >  <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> </div> </nav> {% {% endblock %} <script SRC ="{% static 'js/jquery-3.4.1.min.js' %}"></script> <script SRC ="{% static 'plugin/bootstrap/css/bootstrap.min.css' %}"></script> {% block js %}{% endblock %} </body> </html> ```Copy the code

2.4 the URL to

  • MyDjango/MyDjango/urls.pyThe name of my project isMyDjango
    """MyDjango URL Configuration The `urlpatterns` list routes URLs to views. For more information please see: https://docs.djangoproject.com/en/1.11/topics/http/urls/ Examples: the Function views 1. Add an import: from my_app import views 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') Class-based views 1. Add an import: from other_app.views import Home 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') Including another URLconf 1. Import the include() function: from django.conf.urls import url, include 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) """
    from django.conf.urls import url, include
    from django.contrib import admin
    
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^/', include('web.urls')),]Copy the code
  • Create it in the Web folderurls.pyFile to manage the route of the app (view function we’ll write below)
    # -*- coding: UTF-8 -*-
    "' = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = @ Project - > File: MyDjango - > urls @ IDE: PyCharm @ the Author: Ruochen @ Date: 2020/7/2 1:18 @ Desc: = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = ' ' '
    from django.conf.urls import url
    from web.views import account
    
    urlpatterns = [
        url(r'^register/$', account.register, name='register'),  # register
    ]
    Copy the code

2.5 Model Preparation [Models.py]

  • User registration, to fill in the data are
    • The user name
    • email
    • Mobile phone no.
    • password
  • inweb/models.pyCreate one in the fileUserInfoClass, code as follows
    from django.db import models
    
    class UserInfo(models.Model) :
        username = models.CharField(verbose_name='Username', max_length=32)
        email = models.EmailField(verbose_name='email', max_length=32)
        mobile_phone = models.CharField(verbose_name='Mobile phone Number', max_length=32)
        password = models.CharField(verbose_name='password', max_length=32)
    
        def __str__(self) :
            return self.username
    Copy the code
  • Migrating a Database
    python manage.py makemigrations
    python manage.py migrate
    Copy the code

2.5 View Functions

  • Under the webviews.pyDelete file, create oneviewsFolders for easy management of our view and then inviewsCreate one under the folderaccount.pyThe file acts as a registered view with the following code 🙁RegisterModelFormandregister.htmlIt will be later)
    from django.shortcuts import render
    from web.forms.account import RegisterModelForm
    
    def register(request) :
        form = RegisterModelForm()
        return render(request, 'register.html', {'form': form})
    Copy the code

2.6 ModelForm [Simple Validation & Style Addition]

  • The front end of the page will loop through the Form to generate the data, but it is a bit ugly to generate the data directly, and the data will need to do some basic verification.
  • inwebCreate one under the folderformsFolder,formsCreate in folderaccount.pyFile, code as follows
    • 1: Fields are processed, for example, the mobile phone number is verified and the password isPasswordInputForm etc.
    • Two: add to each fieldform-controlStyle, front-end page display is a little more beautiful
    • 3: addcodeVerification code field
    # -*- coding: UTF-8 -*-
    "' = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = @ Project - > File: MyDjango - > account @ IDE: PyCharm @ the Author: Ruochen @ Date: 2020/7/2 children @ Desc: = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = ' ' '
    from django import forms
    from django.core.validators import RegexValidator
    
    from web import models
    
    class RegisterModelForm(forms.ModelForm) :
        mobile_phone = forms.CharField(
            label='Mobile phone Number',
            validators=[RegexValidator(r'^(1[3|4|5|6|7|8|9])\d{9}$'.'Mobile phone number format error'), ])
        password = forms.CharField(
            label='password', widget=forms.PasswordInput())
    
        confirm_password = forms.CharField(
            label='Double password',
            widget=forms.PasswordInput())
        code = forms.CharField(
            label='Captcha',
            widget=forms.TextInput())
    
        class Meta:
            model = models.UserInfo
            fields = ['username'.'email'.'password'.'confirm_password'.'mobile_phone'.'code']
    
        def __init__(self, *args, **kwargs) :
            super().__init__(*args, **kwargs)
            for name, field in self.fields.items():
                field.widget.attrs['class'] = 'form-control'
                field.widget.attrs['placeholder'] = 'Please enter {}'.format(field.label,)
    
    Copy the code
  • There is no problem with using the above code, however, for addingform-controlWe can wrap it in a class so that when we need to add styles, we can just inherit from that class.
  • Modify as follows, inweb/formsSo let’s create onebootstrap.pyFile with the following code:
    # -*- coding: UTF-8 -*-
    "' = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = @ Project - > File: MyDjango - > the bootstrap @ IDE: PyCharm @ the Author: Ruochen @ Date: 2020/7/3 16:25 @ Desc: = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = ' ' '
    class BootStrapForm(object) :
    
        bootstrap_class_exclude = []
    
        def __init__(self, *args, **kwargs) :
            super().__init__(*args, **kwargs)
            for name, field in self.fields.items():
                if name in self.bootstrap_class_exclude:
                    continue
                old_class = field.widget.attrs.get('class'.' ')
                field.widget.attrs['class'] = '{} form-control'.format(old_class)
                field.widget.attrs['placeholder'] = 'Please enter {}'.format(field.label,)
    Copy the code
  • thenforms/account.pyModify the file to
    from django import forms
    from django.core.validators import RegexValidator
    
    from web import models
    from web.forms.bootstrap import BootStrapForm
    
    class RegisterModelForm(BootStrapForm, forms.ModelForm) :
        password = forms.CharField(
            label='password',
            min_length=8,
            max_length=64,
            error_messages={
                'min_length': "Password length should not be less than 8 characters.".'max_length': "Password length cannot exceed 64 characters."
            },
            widget=forms.PasswordInput())
    
        confirm_password = forms.CharField(
            label='Double password',
            min_length=8,
            max_length=64,
            error_messages={
                'min_length': "Repeat password must be at least 8 characters long.".'max_length': "Repeat password length cannot exceed 64 characters."
            },
            widget=forms.PasswordInput()
        )
    
        mobile_phone = forms.CharField(
            label='Mobile phone Number',
            validators=[RegexValidator(r'^(1[3|4|5|6|7|8|9])\d{9}$'.'Mobile phone number format error'), ])
    
        code = forms.CharField(
            label='Captcha',
            widget=forms.TextInput())
    
        class Meta:
            model = models.UserInfo
            fields = ['username'.'email'.'password'.'confirm_password'.'mobile_phone'.'code']
    Copy the code

2.7 Front-end Page

  • intemplatesCreate a folderregister.htmlFolder that inherits frombasic.html
  • Front-end page for the display of fields, a direct circular displayformThe data generated by the form will do
    {% extends' Layout /basic.html' %} {% load static %} {% block title %} {% block CSS %}<link rel="stylesheet" href="{% static 'css/account.css' %}">
    {% endblock %}
    
    
    {% block content %}
        <div class="account">
            <div class="title">User registration</div>
            <form id="form" method="post" novalidate>
                {% csrf_token %}
                {% for field in form %}
                    {% if field.name == 'code' %}
                        <div class="form-group">
                            <label for="{{ field.id_for_label }}">{{ field.label }}</label>
                            <div class="row">
                                <div class="col-xs-7">
                                    {{ field }}
                                    <span class="error-msg">{{ field.errors.0 }}</span>
                                </div>
                                <div class="col-xs-5">
                                    <input id="smsBtn" type="button" class="btn btn-default" value="Click to get the verification code"/>
                                </div>
                            </div>
                        </div>
                    {% else %}
                        <div class="form-group">
                            <label for="{{ field.id_for_label }}">{{ field.lable }}</label>
                            {{ field }}
                            <span class="error-msg">{{ field.errors.0 }}</span>
                        </div>
                    {% endif %}
                {% endfor %}
    
                <div class="row">
                    <div class="col-xs-3">
                        <input id="submit" type="button" class="btn btn-primary" value="-"/>
                    </div>
                </div>
            </form>
        </div>
    
    {% endblock %}
    
    
    {% block js %}
    
    {% endblock %}
    
    Copy the code

3. Obtain the verification code

3.1 train of thought

  • The button to obtain the verification code is bound with an event. In the front page, after the user clicks to obtain the verification code, the verification code is sent to the user’s mobile phone number through Tencent cloud SMS, and the 60s countdown is displayed on the page, and ajax requests are sent to the back end
  • Verify the mobile phone number (to determine whether the mobile phone number has been registered) and verify the SMS template (some credentials of Tencent cloud SMS).

3.2 Specific Implementation

3.2.1 Front-end Code

  • inregister.htmlAdd the js code as follows
    {% extends' Layout /basic.html' %} {% load static %} {% block title %} {% block CSS %}<link rel="stylesheet" href="{% static 'css/account.css' %}">
        <style>
            .error-msg {
                color: red;
                position: absolute;
                font-size: 13px;
            }
        </style>
    {% endblock %}
    
    
    {% block content %}
        <div class="account">
            <div class="title">User registration</div>
            <form id="form" method="POST" novalidate>
                {% csrf_token %}
                {% for field in form %}
                    {% if field.name == 'code' %}
                        <div class="form-group">
                            <label for="{{ field.id_for_label }}">{{ field.label }}</label>
                            <div class="row">
                                <div class="col-xs-7">
                                    {{ field }}
                                    <span class="error-msg"></span>
                                </div>
                                <div class="col-xs-5">
                                    <input id="btnSms" type="button" class="btn btn-default" value="Click to get the verification code">
                                </div>
                            </div>
                        </div>
                    {% else %}
                        <div class="form-group">
                            <label for="{{ field.id_for_label }}">{{ field.label }}</label>
                            {{ field }}
                            <span class="error-msg"></span>
                        </div>
                    {% endif %}
                {% endfor %}
    
                <div class="row">
                    <div class="col-xs-3">
                        <input id="submit" type="button" class="btn btn-primary" value="-"/>
                    </div>
                </div>
            </form>
        </div>
    {% endblock %}
    
    
    {% block js %}
        <script>
            // The function is automatically executed after the page frame is loaded
            $(function () {
    
                bindClickBtnSms();
            });
    
            /* Click the button binding event */ to get the captcha
            function bindClickBtnSms() {$('#btnSms').click(function () {$('.error-msg').empty();
    
                    // Get the phone number entered by the user
                    // Find the ID of the input box, obtain the value according to the ID, how to find the mobile phone ID?
                    // Django ModelForm generates fields with id_ + field names by default.
                    var mobilePhone = $('#id_mobile_phone').val();
    
                    // Send the Ajax request, and send the mobile phone number
                    $.ajax({
                        url: "{% url 'send_sms' %}".// Equivalent to /send/ SMS /
                        type: "GET".data: {mobile_phone: mobilePhone, tpl: "register"},  // Mobile phone number and registration template
                        dataType: "JSON".// Deserialize the data returned by the server into a dictionary
                        success: function (res) {
                            // The function that is automatically executed after the Ajax request is successfully sent: res is the value returned by the back end
                            if(res.status) {
                                sendSmsRemind();
                            } else {
                                // Error message
                                // console.log(res); // {status: False, error: {mobile_phone: [" error ",]}
                                $.each(res.error, function (key, value) {$("#id_" + key).next().text(value[0]); })}})})}/* Countdown */
            function sendSmsRemind() {
                var $smsBtn = $('#btnSms');
                // Make the button unclickable
                $smsBtn.prop('disabled'.true);
                var time = 60;
                var remind = setInterval(function () {
                    $smsBtn.val(time + 'Resend in seconds');
                    time = time - 1;
                    if (time < 1) {
                        clearInterval(remind);
                        $smsBtn.val('Click on the captcha code').prop('disabled'.false); }},1000)}</script>
    
    {% endblock %}
    
    Copy the code

The 60s countdown on the front page uses the timer function, as follows

var obj = setInterval(function(){  // Create a timer, which is equivalent to executing function every second
   console.log(123);
}, 1000)

clearInterval(obj);  // Turn off the timer
Copy the code

Then, for the countdown function of 60s, we can use the following code to achieve it

var time = 60;
var obj = setInterval(function(){
   time = time - 1;
   if(time < 1) {
       clearInterval(obj); }},1000)
Copy the code
  • One of theaccount.cssI wrote my own CSS style inweb/staticCreate a new folder under this foldercssThe folder is used to store your own CSS styles, and then create a new oneaccount.cssFile, code as follows
    .account {
        width: 400px;
        margin-top: 30px;
        margin-left: auto;
        margin-right: auto;
        border: 1px solid #f0f0f0;
        padding: 10px 30px 30px 30px;
        -webkit-box-shadow: 5px 10px 10px rgba(0.0.0.05);
        box-shadow: 5px 10px 10px rgba(0.0.0.05);
    }
    
    .account .title {
        font-size: 25px;
        font-weight: bold;
        text-align: center;
    }
    
    .account .form-group {
        margin-bottom: 20px;
    }
    Copy the code

3.2.2 Back-end code

3.2.2.1 URL
  • First, it writes that the front end page sends an Ajax request to the back end via JS at the address/send/sms/, so we need to add one firsturl.web/urls.pyThe code is as follows:
    from django.conf.urls import url
    from web.views import account
    
    urlpatterns = [
        url(r'^register/$', account.register, name='register'),  # register
        url(r'^send/sms/$', account.send_sms, name='send_sms'),  # register
    ]
    Copy the code
3.2.2.2 View Functions
  • The route is added above, and the view function is added belowweb/views/account.pyAdd the code to the file as follows
    from django.shortcuts import render, HttpResponse
    from django.http import JsonResponse
    from web.forms.account import RegisterModelForm, SendSmsForm
    
    def register(request) :
        "" "register "" "
        form = RegisterModelForm()
        return render(request, 'register.html', {'form': form})
    
    def send_sms(request) :
        """ Send a text message """
        form = SendSmsForm(request, data=request.GET)
        # Only verify the phone number: cannot be blank, format is correct
        if form.is_valid():
            return JsonResponse({'status': True})
    
        return JsonResponse({'status': False.'error': form.errors})
    Copy the code
3.2.2.3 Configuration File
3.2.2.3.1 Tencent Cloud SMS configuration file
  • For Tencent cloud SMS profile, we should place it inlocal_settings.pyFile (local_settings.pyThe role of files, which I mentioned in the last article, is also insettings.pyDeclaration in the file
  • local_settings.pyThe file configuration code is as follows
import os

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

# --------- sms -----------
# Tencent cloud SMS app app_id
TENCENT_SMS_APP_ID = 'Own app_id'
# Tencent cloud SMS app app_key
TENCENT_SMS_APP_KEY = 'Own app_key'
# Tencent cloud SMS signature content
TENCENT_SMS_SIGN = 'xxxx'

# SMS template
TENCENT_SMS_TEMPLATE = {
    'register': 'xxxx'.'login': 'xxxx',}Copy the code

For details about Tencent cloud SMS configuration, see this article: Python to operate Tencent cloud SMS (SMS) detailed tutorial

  • App_id & app_key is the AppID & AppKey after the application is created
  • SMS signature content is the content displayed after creating the signature, such as mineLittle ape like dustAs shown in the following

  • The ID of an SMS template is the ID of an SMS template
  • settings.pyThe document should also state the following (settings.pyAdd the following code at the end of the file, assign whatever you want, because we imported it at the endlocal_settings.pyFiles, projects actually uselocal_settings.pyConfiguration in the file, just for the record, because ourlocal_settings.pyThe documents are not to be given.)
# --------- sms -----------
# Tencent cloud SMS app app_id
TENCENT_SMS_APP_ID = 6666
# Tencent cloud SMS app app_key
TENCENT_SMS_APP_KEY = '6666'
# Tencent cloud SMS signature content
TENCENT_SMS_SIGN = 'xxxx'
# SMS template
TENCENT_SMS_TEMPLATE = {
    'register': 666666.'login': 666666,}Copy the code
3.2.2.3.2 Redis configuration file
  • See this article for details on redis:Redis download install & Python operation Redis & Django connect to RedisThat’s what’s used heredjango-redisModule [Remember to install]
  • The redis configuration is placed inlocal_settings.pyIn the file, the code is as follows:
    CACHES = {
        "default": {
            "BACKEND": "django_redis.cache.RedisCache"."LOCATION": "Redis: / / 192.168.1.6:6379".Run the [ipconfig] command on the terminal
            "OPTIONS": {
                "CLIENT_CLASS": "django_redis.client.DefaultClient"."CONNECTION_POOL_KWARGS": {
                    "max_connections": 1000."encoding": 'utf-8'
                },
                "PASSWORD": "root"  # Password, as specified in the above article}}Copy the code
3.2.2.4 SendSmsForm
  • In the view function above we passSendSmsFormI checked it,web/forms/account.pyThe code in the file is as follows
    • 1. Check the mobile phone number and SMS template
    • Two: use Tencent cloud SMS to send short messages to users
      • Create it in the project directoryutilsFolder to hold our utility class, and create another onetencentFolder, created under the foldersms.pyFile, as follows:

      • sms.pyThe file code is as follows
        # -*- coding: UTF-8 -*-
        "' = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = @ Project - > File: MyDjango - > SMS @ IDE: PyCharm @ the Author: Ruochen @ Date: 2020/6/21 15:57 @ Desc: = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = ' ' '
        
        import ssl
        
        # ssl._create_default_https_context = ssl._create_unverified_context
        from qcloudsms_py import SmsMultiSender, SmsSingleSender
        from qcloudsms_py.httpclient import HTTPError
        from django.conf import settings
        
        def send_sms_single(phone_num, template_id, template_param_list) :
            """ Send a single SMS message :param phone_num: mobile phone number :param template_id: Tencent cloud SMS template ID :param template_param_list: Parameters required by the SMS template, for example, [Verification code: {1}, Description: {2}], pass [888,666] to format the template in sequence :return: ""
            appid = settings.TENCENT_SMS_APP_ID  # apply ID to yourself
            appkey = settings.TENCENT_SMS_APP_KEY  # Apply the Key yourself
            sms_sign = settings.TENCENT_SMS_SIGN  # Signature content filled in when creating a signature on Tencent Cloud (if using an official account, this value is generally the full name or abbreviation of the official account)
            sender = SmsSingleSender(appid, appkey)
            try:
                response = sender.send_with_param(86, phone_num, template_id, template_param_list, sign=sms_sign)
            except HTTPError as e:
                response = {'result': 1000.'errmsg': "Network error sending failed"}
            return response
        
        def send_sms_multi(phone_num_list, template_id, param_list) :
            "" batch sending SMS messages :param phone_num_list: mobile phone number list: param template_id: Tencent cloud SMS template ID :param param_list: list of parameters required by the SMS template, for example: [Verification code: {1}, description: {2}], then pass [888,666] to format the template in order :return: """
            appid = settings.TENCENT_SMS_APP_ID  # apply ID to yourself
            appkey = settings.TENCENT_SMS_APP_KEY  # Apply the Key yourself
            sms_sign = settings.TENCENT_SMS_SIGN  # Signature content filled in when creating a signature on Tencent Cloud (if using an official account, this value is generally the full name or abbreviation of the official account)
            sender = SmsMultiSender(appid, appkey)
            try:
                response = sender.send_with_param(86, phone_num_list, template_id, param_list, sign=sms_sign)
            except HTTPError as e:
                response = {'result': 1000.'errmsg': "Network error sending failed"}
            return response
        Copy the code
    • Third, the verification code is stored in redis database, and the timeout time is 60s [that is, it will disappear automatically after 60s], so that when we submit the form, we can compare the verification code entered by the user with that saved in Redis, and if the time exceeds 60s, the user has to obtain the verification code again
      • You can see how Redis works in this article: Redis download install & Python operation Redis & Django connect to Redis
    #-*- coding: UTF-8 -*-
    "' = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = @ Project - > File: MyDjango - > account @ IDE: PyCharm @ the Author: Ruochen @ Date: 2020/7/2 children @ Desc: = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = ' ' '
    import random
    from django import forms
    from django.core.validators import RegexValidator
    from django.core.exceptions import ValidationError
    from django.conf import settings
    
    from django_redis import get_redis_connection
    
    from web import models
    from web.forms.bootstrap import BootStrapForm
    from utils.tencent.sms import send_sms_single
    
    class RegisterModelForm(BootStrapForm, forms.ModelForm) :
        password = forms.CharField(
            label='password',
            min_length=8,
            max_length=64,
            error_messages={
                'min_length': "Password length should not be less than 8 characters.".'max_length': "Password length cannot exceed 64 characters."
            },
            widget=forms.PasswordInput())
    
        confirm_password = forms.CharField(
            label='Double password',
            min_length=8,
            max_length=64,
            error_messages={
                'min_length': "Repeat password must be at least 8 characters long.".'max_length': "Repeat password length cannot exceed 64 characters."
            },
            widget=forms.PasswordInput()
        )
    
        mobile_phone = forms.CharField(
            label='Mobile phone Number',
            validators=[RegexValidator(r'^(1[3|4|5|6|7|8|9])\d{9}$'.'Mobile phone number format error'), ])
    
        code = forms.CharField(
            label='Captcha',
            widget=forms.TextInput())
    
        class Meta:
            model = models.UserInfo
            fields = ['username'.'email'.'password'.'confirm_password'.'mobile_phone'.'code']
    
    class SendSmsForm(forms.Form) :
        mobile_phone = forms.CharField(label='Mobile phone Number', validators=[RegexValidator(r'^(1[3|4|5|6|7|8|9])\d{9}$'.'Mobile phone number format error'),])def __init__(self, request, *args, **kwargs) :
            super().__init__(*args, **kwargs)
            self.request = request
    
        def clean_mobile_phone(self) :
            """ Hook for checking mobile phone number """
            mobile_phone = self.cleaned_data['mobile_phone']
    
            Determine if there is a problem with the SMS template
            tpl = self.request.GET.get('tpl')
            template_id = settings.TENCENT_SMS_TEMPLATE.get(tpl)
            if not template_id:
                # self.add_error('mobile_phone', 'SMS template error ')
                raise ValidationError('SMS template error')
    
            Check if there is a mobile phone number in the database
            exists = models.UserInfo.objects.filter(mobile_phone=mobile_phone).exists()
            if exists:
                raise ValidationError('Mobile phone number already exists')
    
            # texting
            code = random.randrange(1000.9999)
    
            # Send text messages
            sms = send_sms_single(mobile_phone, template_id, [code, ])
            if sms['result'] != 0:
                raise ValidationError('SMS sending failed, {}'.format(sms['errmsg']))
    
            # write to redis (Django-redis)
            conn = get_redis_connection()
            conn.set(mobile_phone, code, ex=60)
    
            return mobile_phone
    Copy the code

4. Click Register

4.1 Front end: Get data & send Ajax requests

  • Collect data from the form (find each field)
  • Data is sent to the background via Ajax [POST request]
  • register.htmlAdd the click register event function to the js part of the file, the code is as followsbindClickSubmitFunction to execute automatically after the page frame has loaded.

So instead of writing a URL, I’m going to reuse /register/, just to figure out what kind of request the user is sending

  • The user sends the address anywayGETRequest, then we directly let it jump to the registration page
  • When the user clicks register, the message isPOSTRequest, then we carry out form validation & write to the database and other operations
{% block js %}
    <script>
        // The function is automatically executed after the page frame is loaded
        $(function () {
            bindClickBtnSms();
            bindClickSubmit();
        });

        /* Click Submit */
        function bindClickSubmit() {$('#btnSubmit').click(function () {$('.error-msg').empty();

                // Collect the data in the form (find each field)
                // Data is sent to the background via Ajax
                $.ajax({
                    url: "{% url 'register' %}".type: "POST".data: $('#regForm').serialize(),  // Get all key values in the form, including all field data + CSRF token
                    dataType: "JSON".success: function (res) {
                        if (res.status) {
                            location.href = res.data;
                        } else {
                            $.each(res.error, function (key, value) {$("#id_" + key).next().text(value[0]); })}})})}/* Click the button binding event */ to get the captcha
        function bindClickBtnSms() {$('#btnSms').click(function () {$('.error-msg').empty();

                // Get the phone number entered by the user
                // Find the ID of the input box, obtain the value according to the ID, how to find the mobile phone ID?
                // Django ModelForm generates fields with id_ + field names by default.
                var mobilePhone = $('#id_mobile_phone').val();

                // Send the Ajax request, and send the mobile phone number
                $.ajax({
                    url: "{% url 'send_sms' %}".// Equivalent to /send/ SMS /
                    type: "GET".data: {mobile_phone: mobilePhone, tpl: "register"},  // Mobile phone number and registration template
                    dataType: "JSON".// Deserialize the data returned by the server into a dictionary
                    success: function (res) {
                        // The function that is automatically executed after the Ajax request is successfully sent: res is the value returned by the back end
                        if (res.status) {
                            sendSmsRemind();
                        } else {
                            // Error message
                            // console.log(res); // {status: False, error: {mobile_phone: [" error ",]}
                            $.each(res.error, function (key, value) {$("#id_" + key).next().text(value[0]); })}})})}/* Countdown */
        function sendSmsRemind() {
            var $smsBtn = $('#btnSms');
            $smsBtn.prop('disabled'.true);
            var time = 60;
            var remind = setInterval(function () {
                $smsBtn.val(time + 'Resend in seconds');
                time = time - 1;
                if (time < 1) {
                    clearInterval(remind);
                    $smsBtn.val('Click on the captcha code').prop('disabled'.false); }},1000)}</script>
{% endblock %}
Copy the code

4.2 the back-end

4.2.1 Data verification

  • The verification is as follows:
    • The user name, email address, and phone number are validated in the hook function
    • The password is encrypted by MD5 and returned
      • Md5 encryption is separately encapsulated inutilsAdd to folderencrypt.pyfile

– python import uuid import hashlib

from django.conf import settings def md5(string): Hash_object = hashlib. MD5 (settings.secret_key.encode (' utF-8 ')) hash_object.update(string.encode(' utF-8 ')) return hash_object.hexdigest() def uid(string): Data = "{}-{}". Format (STR (uuid.uuid4()), string) return MD5 (data) - Verification code Is obtained by redis based on the phone number (key) and compared with the user input.Copy the code
  • Modify theforms/account.pyThe file is as follows :(modified onlyRegisterModelFormClass, otherwise unchanged.)
from utils import encrypt

class RegisterModelForm(BootStrapForm, forms.ModelForm) :
    password = forms.CharField(
        label='password',
        min_length=8,
        max_length=64,
        error_messages={
            'min_length': "Password length should not be less than 8 characters.".'max_length': "Password length cannot exceed 64 characters."
        },
        widget=forms.PasswordInput())

    confirm_password = forms.CharField(
        label='Double password',
        min_length=8,
        max_length=64,
        error_messages={
            'min_length': "Repeat password must be at least 8 characters long.".'max_length': "Repeat password length cannot exceed 64 characters."
        },
        widget=forms.PasswordInput()
    )

    mobile_phone = forms.CharField(
        label='Mobile phone Number',
        validators=[RegexValidator(r'^(1[3|4|5|6|7|8|9])\d{9}$'.'Mobile phone number format error'), ])

    code = forms.CharField(
        label='Captcha',
        widget=forms.TextInput())

    class Meta:
        model = models.UserInfo
        fields = ['username'.'email'.'password'.'confirm_password'.'mobile_phone'.'code']

    def clean_username(self) :
        username = self.cleaned_data['username']

        exists = models.UserInfo.objects.filter(username=username).exists()
        if exists:
            raise ValidationError('Username already exists')
            # self.add_error('username', 'username already exists ')
        return username

    def clean_email(self) :
        email = self.cleaned_data['email']

        exists = models.UserInfo.objects.filter(email=email).exists()
        if exists:
            raise ValidationError('Mailbox already exists')
        return email

    def clean_password(self) :
        pwd = self.cleaned_data['password']
        # encrypt & return
        return encrypt.md5(pwd)

    def clean_confirm_password(self) :
        # pwd = self.cleaned_data['password']
        pwd = self.cleaned_data.get('password')

        confirm_pwd = encrypt.md5(self.cleaned_data['confirm_password'])

        ifpwd ! = confirm_pwd:raise ValidationError('Two different passwords')
        return confirm_pwd

    def clean_mobile_phone(self) :
        mobile_phone = self.cleaned_data['mobile_phone']
        exists = models.UserInfo.objects.filter(mobile_phone=mobile_phone).exists()
        if exists:
            raise ValidationError('Mobile phone number registered')
        return mobile_phone

    def clean_code(self) :
        code = self.cleaned_data['code']
        # mobile_phone = self.cleaned_data['mobile_phone']

        mobile_phone = self.cleaned_data.get('mobile_phone')
        if not mobile_phone:
            return code

        conn = get_redis_connection()
        redis_code = conn.get(mobile_phone)
        if not redis_code:
            raise ValidationError('Verification code invalid or not sent, please resend')

        redis_str_code = redis_code.decode('utf-8')

        ifcode.strip() ! = redis_str_code:raise ValidationError('Verification code error, please re-enter')

        return code
Copy the code

4.2.2 Writing to the Database

  • After data verification is successful, a record can be created in the database and jump to/login/Page (login page detailed in the next blog post)
  • web/views/account.pyThe code in the file is modified as follows :(only modifiedregisterThe contents of the function remain the same.)
def register(request) :
    "" "register "" "
    if request.method == 'GET':
        form = RegisterModelForm()
        return render(request, 'register.html', {'form': form})

    form = RegisterModelForm(data=request.POST)
    if form.is_valid():
        Write to database (password is ciphertext)
        # data = form.cleaned_data
        # data.pop('code')
        # data.pop('confirm_password')
        # instance = models.UserInfo.objects.create(**data)
        # save() is the same code as above and will automatically weed out data that is not in the database
        Select * from user where username = 'user';
       	form.save()

        return JsonResponse({'status': True.'data': '/login/'})

    return JsonResponse({'status': False.'error': form.errors})
Copy the code