链接:https://pan.baidu.com/s/1TJCCZTt9LBtM9enxAY-vlQ?pwd=a4bu

提取码:a4bu


写一个登录页面

from django.urls import path
from app01.views import account
urlpatterns = [
    path("login/", account.login, name="login"),
]



#forms.account.py
from django import forms


class LoginForm(forms.Form):
    role = forms.ChoiceField(
        label="角色",
        required=True,
        choices=(("2", "客户"), ("1", "管理员"),)
    )

    username = forms.CharField(
        label="用户名",
        widget=forms.TextInput
    )
    passwod = forms.CharField(
        label="密码",
        widget=forms.PasswordInput
    )

#views.account.py
from django.shortcuts import render
from app01.forms.account import LoginForm


def login(request):
    if request.method == "GET":
        form = LoginForm()
        return render(request, "login.html", {"form": form})

    
#html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h3>用户登录</h3>
<form method="post" novalidate>
{% csrf_token %}
{% for field in form %}
    <p>
    <label>{{ field.label }}</label>
    {{ field }}
    <span>{{ field.errors.0 }}</span>
    </p>
{% endfor %}
<input type="submit" value="提交">
</form>
</body>
</html>

项目的结构:

订单刷播放项目的回顾二_django

订单刷播放项目的回顾二_django_02

短信登录&&密码登录跳转

后端代码逻辑类似

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form method="post" novalidate>
{% csrf_token %}
{% for field in form %}
    <p>
    <label>{{ field.label }}</label>
    {{ field }}
    <span>{{ field.errors.0 }}</span>
    </p>
{% endfor %}
<input type="submit" value="提交">
{#这里用的就是urls.py的别名,name=xxx#}
<a href="{% url 'login' %}">账户登录</a>
</form>
</body>
</html>

订单刷播放项目的回顾二_html_03

用户登录的逻辑

def login(request):
    if request.method == "GET":
        form = LoginForm()
        return render(request, "login.html", {"form": form})
    # 格式是否正确
    form = LoginForm(data=request.POST)
    if not form.is_valid():
        return render(request, "login.html", {"form": form})
    # 数据库校验,这里要选择表,管理员 || 普通客户
    data_dict = form.cleaned_data
    role = data_dict.pop("role")

    if role == "1":
        user_object = models.Administrator.objects.filter(**data_dict).filter(active=1).first()
    else:
        user_object = models.Customer.objects.filter(**data_dict).filter(active=1).first()
    # 数据输入错误
    if not user_object:
        form.add_error("password", "用户名或密码错误")
        return render(request, "login.html", {"form": form})
    # 通过校验,存入session
    mapping = {"1": "ADMIN", "2": "CUSTOMER"}
    request.session['user_info'] = {
        "role": mapping[role],
        "id": user_object.id,
        "name": user_object.username,
    }
    #成功,跳转到后台路径
    from django.conf import settings
    #HOME_URL在settings.py中设置 HOME_URL = "/home/"
    return redirect(settings.HOME_URL)
    
#倘若想保留密码,即使输错了
class LoginForm(forms.Form):
    role = forms.ChoiceField(
        label="角色",
        required=True,
        choices=(("2", "客户"), ("1", "管理员"),)
    )

    username = forms.CharField(
        label="用户名",
        widget=forms.TextInput
    )
    password = forms.CharField(
        label="密码",
        #这个是重点
        widget=forms.PasswordInput(render_value=True)
    )

订单刷播放项目的回顾二_验证码_04

订单刷播放项目的回顾二_django_05

密码不要存明文,存哈希值
import hashlib


# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-&2p7a#%zq6v8y14xr(6(s)8ww3e-)1!)t!=utgqurh_pa0e(k@'


def md5(data_string):
    obj = hashlib.md5(SECRET_KEY.encode('utf-8'))
    obj.update(data_string.encode('utf-8'))
    return obj.hexdigest()

print(md5("1111"))

写在校验的时候

from utils.encrypt import md5


class LoginForm(forms.Form):
    role = forms.ChoiceField(
        label="角色",
        required=True,
        choices=(("2", "客户"), ("1", "管理员"),)
    )

    username = forms.CharField(
        label="用户名",
        widget=forms.TextInput
    )
    password = forms.CharField(
        label="密码",
        widget=forms.PasswordInput(render_value=True)
    )

    def clean_password(self):
        old_password = self.cleaned_data['password']
        return md5(old_password)

短信登录的逻辑

发送短信验证码(ajax请求):
   填写手机号,发送短信,手机号用ajax发送后台
   生成随机数字 + 调用第三方api短信
   手机号 + 短信验证码 + redis保存
登录:
   填写手机号 + 填写短信验证码
   是否失效
获取手机号 + ajax请求流程:
    引入jquery,自行搜索
    

sms_login.html

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form method="post" novalidate>
    {% csrf_token %}
    {% for field in form %}
        <p>
            <label>{{ field.label }}</label>
            {{ field }}
            <span>{{ field.errors.0 }}</span>
        </p>
    {% endfor %}
    {#绑定一个事件#}
    <input id="btnSendSms" type="button" value="点击获取验证码" class="btn btn-default"/>
    <input type="submit" value="提交">
    {#这里用的就是urls.py的别名,name=xxx#}
    <a href="{% url 'login' %}">账户登录</a>
</form>
<script src="{% static 'js/jquery.js' %}"></script>
<script>
    $(function () {
        bindSendSmsEvent();
    })

    function bindSendSmsEvent() {
    $("#btnSendSms").click(function (){
        console.log(123456);
    });
    }

</script>
</body>
</html>

订单刷播放项目的回顾二_html_06

记得添加路径

获取电话
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form method="post" novalidate>
    {% csrf_token %}
    {% for field in form %}
        <p>
            <label>{{ field.label }}</label>
            {{ field }}
            <span>{{ field.errors.0 }}</span>
        </p>
    {% endfor %}
    {#绑定一个事件#}
    <input id="btnSendSms" type="button" value="点击获取验证码" class="btn btn-default"/>
    <input type="submit" value="提交">
    {#这里用的就是urls.py的别名,name=xxx#}
    <a href="{% url 'login' %}">账户登录</a>
</form>
<script src="{% static 'js/jquery.js' %}"></script>
<script>
    $(function () {
        bindSendSmsEvent();
    })

    function bindSendSmsEvent() {
    $("#btnSendSms").click(function (){
       $.ajax({
           url: "/send/sms/",
           type: "POST",
           data:{
               {#固定方法#}
               mobile:$("#id_mobile").val(),
               role:$("#id_role").val(),
           },
           dataType: "JSON",
           success: function (res){
               console.log(res);
           }
       })
    });
    }

</script>
</body>
</html>

订单刷播放项目的回顾二_html_07

我们这里假装发了一个验证码

#forms.form
class SendSmsForm(forms.Form):
    role = forms.ChoiceField(
        label="角色",
        required=True,
        choices=(("2", "客户"), ("1", "管理员"),)
    )

    mobile = forms.CharField(
        label="电话",
        widget=forms.TextInput,
        required=True,
        validators=[RegexValidator(r'1[3578]\d{9}$', "手机格式错误")]
    )

    # 从已注册的用户中获取
    def clean_mobile(self):
        old_mobile = self.cleaned_data['mobile']
        role = self.cleaned_data['role']

        if role == "1":
            exists = models.Administrator.objects.filter(active=1).filter(mobile=old_mobile).first()
        else:
            exists = models.Customer.objects.filter(active=1).filter(mobile=old_mobile).first()

        if not exists:
            raise ValidationError("手机号不存在")

        sms_code = str(random.randint(1000, 9999))
        print("验证码:", sms_code)
        # 调用第三方接口发送验证码,这里没用上,自定义的一个
        is_ok = send_sms(old_mobile, sms_code)
        if not is_ok:
            raise ValidationError("短信发送失败!")
        # 短信验证码写入redis,设置超时时间
        conn = get_redis_connection("default")
        # 60秒有效
        conn.set(old_mobile, sms_code, ex=60)
        return old_mobile
#views
#@csrf_exempt是django防止csrf攻击,我们去除验证。不然发不了
@csrf_exempt
def send_sms(request):
    print(request.POST)
    # 校验格式
    # 限制次数,一般第三方平台有这个功能
    form = SendSmsForm(data=request.POST)
    if form.is_valid():
        return JsonResponse({"status": True, "msg": "OK"})
    else:
        return JsonResponse({"status": False, "msg": form.errors})
电话验证码登录

订单刷播放项目的回顾二_html_08


后端校验
def sms_login(request):
    if request.method == "GET":
        form = SmsLoginForm()
        return render(request, "sms_login.html", {"form": form})
    form = SmsLoginForm(data=request.POST)
    if not form.is_valid():
        return JsonResponse({"status": False, "msg": "fail"})

    role = form.cleaned_data["role"]
    mobile = form.cleaned_data["mobile"]
    if role == "1":
        user_object = models.Administrator.objects.filter(mobile=mobile).filter(active=1).first()
    else:
        user_object = models.Customer.objects.filter(mobile=mobile).filter(active=1).first()
    if not user_object:
        return JsonResponse({"status": False, "msg": {"mobile": ["手机号不存在"]}})
    # 通过校验,存入session
    mapping = {"1": "ADMIN", "2": "CUSTOMER"}
    request.session['user_info'] = {
        "role": mapping[role],
        "id": user_object.id,
        "name": user_object.username,
    }
    return JsonResponse({"status": True, "msg": "OK", "data":settings.HOME_URL})

订单刷播放项目的回顾二_django_09

实现登录成功,跳转

<form method="post" id="SmsForm" novalidate>
    {% csrf_token %}
    {% for field in form %}
        <p>
            <label>{{ field.label }}</label>
            {{ field }}
            <span>{{ field.errors.0 }}</span>
        </p>
    {% endfor %}
    {#绑定一个事件#}
    <input id="btnSendSms" type="button" value="点击获取验证码" class="btn btn-default"/>
    <input type="button" value="提交" id="btnSubmit">
    {#这里用的就是urls.py的别名,name=xxx#}
    <a href="{% url 'login' %}">账户登录</a>
</form>

function bindSubmitEvent() {
        $("#btnSubmit").click(function () {
            $.ajax({
                url: "/sms/login/",
                type: "POST",
                data: $("#SmsForm").serialize(),
                dataType: "JSON",
                success: function (res) {
                    if(res.status){
                        {#这里实现跳转#}
                        location.href = res.data;
                    }else {
                        console.log(res.msg)
                    }
                }
            })
        })
    }

订单刷播放项目的回顾二_html_10

完善功能
短信验证码重复使用
redis中删掉就行了

订单刷播放项目的回顾二_django_11


倒计时效果

订单刷播放项目的回顾二_验证码_12

function bindSendSmsEvent() {
        $("#btnSendSms").click(function () {
            $.ajax({
                url: "/send/sms/",
                type: "POST",
                data: {
                    {#固定方法#}
                    mobile: $("#id_mobile").val(),
                    role: $("#id_role").val(),
                },
                dataType: "JSON",
                success: function (res) {
                    if (res.status) {
                        {#点击之后不允许再点击#}
                        $("#btnSendSms").prop("disabled", true);
                        {#这里自定义一个定时器#}
                        let time = 60
                        let remind = setInterval(function () {
                            {#动态效果#}
                            $("#btnSendSms").val(time + "秒重新发送");
                            time = time - 1;
                             if (time < 1) {
                            $("#btnSendSms").val("点击获取验证码");
                            {#重新记录时间#}
                            clearInterval(remind);
                            {#这时候可以重新点击#}
                            $("#btnSendSms").prop("disabled", false);

                        }
                        }, 1000);
                        {#出现负数,显然不合理。需要重新赋值#}
                    } else {
                    }
                }
            })
        });
    }


错误信息展示
//以短信登录为例
function bindSendSmsEvent() {
        $("#btnSendSms").click(function () {
            $(".error-msg").empty()
            $.ajax({
                url: "/send/sms/",
                type: "POST",
                data: {
                    {#固定方法#}
                    mobile: $("#id_mobile").val(),
                    role: $("#id_role").val(),
                },
                dataType: "JSON",
                success: function (res) {
                    if (res.status) {
                        {#点击之后不允许再点击#}
                        $("#btnSendSms").prop("disabled", true);
                        {#这里自定义一个定时器#}
                        let time = 60
                        let remind = setInterval(function () {
                            {#动态效果#}
                            $("#btnSendSms").val(time + "秒重新发送");
                            time = time - 1;
                            if (time < 1) {
                                $("#btnSendSms").val("点击获取验证码");
                                {#重新记录时间#}
                                clearInterval(remind);
                                {#这时候可以重新点击#}
                                $("#btnSendSms").prop("disabled", false);

                            }
                        }, 1000);
                        {#出现负数,显然不合理。需要重新赋值#}
                    } else {
                        {#错误信息展示#}
                        $.each(res.msg, function (k, v) {
                            $("#id_" + k).next().text(v[0]);
                        })
                    }
                }
            })
        });
    }
#settings.py
#中文
LANGUAGE_CODE = 'zh-hans'

订单刷播放项目的回顾二_django_13