06-day04-短信登录
一、短信登录
- 使用与注册相同的短信发送接口
1.1、页面展示
- 第一步 :设计短信登录路由
(Bug_manager) daizhe@daizhedeMacBook-Pro Bug_manager % cat web_app/urls.py
"""Bug_manager URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/3.2/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.conf.urls import url, include
from web_app.views import account
urlpatterns = [
url(r'^send/sms/', account.send_sms, name='send_sms'),
# 短信登录
url(r'^login/sms', account.login_sms, name='login_sms'),
url(r'^register/', account.register, name='register'),
]
- 第二步 :视图,目前仅用于测试
(Bug_manager) daizhe@daizhedeMacBook-Pro Bug_manager % cat web_app/views/account.py
...
...
from web_app.forms.account import RegisterModelForm,SendSmsForm, LoginSMSForm
...
...
def login_sms(request):
""" 短信登录 """
form = LoginSMSForm()
return render(request, 'login_sms.html', {'form': form})
- 第三步 :为短信登录,编写一个用于数据校验的 Form
(Bug_manager) daizhe@daizhedeMacBook-Pro Bug_manager % cat web_app/forms/account.py
from django import forms
from web_app import models
from django.core.validators import RegexValidator
from django.core.exceptions import ValidationError
from django.conf import settings
from django_redis import get_redis_connection
import random
from utils.tencent.sms import send_sms_single
class RegisterModelForm(forms.ModelForm):
password = forms.CharField(label='密码',
min_length=8,
max_length=64,
error_messages={
"min_length": "密码长度不能小于8个字符",
"max_length": "密码长度不能大于64个字符",
},
widget=forms.PasswordInput())
confirm_password = forms.CharField(label='重复密码', widget=forms.PasswordInput())
mobile_phone = forms.CharField(label='手机号', validators=[RegexValidator(r'(1[3|4|5|6|7|8|9])\d{9}$', '手机号格式错误'), ])
code = forms.CharField(label='验证码', widget=forms.TextInput())
# model 定义元数据
class Meta:
# 对应的Model类
model = models.UserInfo
# Model类中哪些字段可以展示,__all__ 表示所有
# fields = '__all__' 也表示默认的展示顺序,可以手动指定展示顺序
fields = ['username', 'email', 'password', 'confirm_password', 'mobile_phone', 'code']
def __init__(self, *args, **kwargs):
"""
重写RegisterModelForm 的 初始化方法
name 表示字段名称
field 表示forms.CharField对象
code = forms.CharField(label='验证码', widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': "请输入验证码"}))
"""
super(RegisterModelForm, self).__init__(*args, **kwargs)
for name, field in self.fields.items():
field.widget.attrs['class'] = 'form-control'
field.widget.attrs['placeholder'] = "请输入{}".format(field.label)
def clean_username(self):
"""
用户名校验的钩子函数
"""
username = self.cleaned_data['username']
exists = models.UserInfo.objects.filter(username=username).exists()
if exists:
raise ValidationError('用户名已存在')
return username
def clean_email(self):
"""
邮箱校验的钩子函数
"""
email = self.cleaned_data['email']
exists = models.UserInfo.objects.filter(email=email).exists()
if exists:
raise ValidationError('邮箱已存在')
return email
def clean_confirm_password(self):
"""
密码和确认密码对比校验
"""
pwd = self.cleaned_data['password']
confirm_pwd = self.cleaned_data['confirm_password']
if pwd != confirm_pwd:
raise ValidationError('两次密码不一致')
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('手机号已注册')
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('验证码失效或未发送,请重新发送')
redis_str_code = redis_code.decode('utf-8')
if code.strip() != redis_str_code:
raise ValidationError('验证码错误,请重新输入')
return code
class SendSmsForm(forms.Form):
mobile_phone = forms.CharField(label='手机号', validators=[RegexValidator(r'(1[3|4|5|6|7|8|9])\d{9}$', '手机号格式错误'),])
# 重写__init__ 仅为在views视图调用Form初始化函数式多传递request参数
def __init__(self, request, *args, **kwargs):
super(SendSmsForm, self).__init__(*args, **kwargs)
self.request = request
def clean_mobile_phone(self):
"""手机号校验钩子函数 (单个字段方法如 def cleand_字段名())"""
mobile_phone = self.cleaned_data['mobile_phone']
# 判断短信模板是否有问题
tpl = self.request.GET.get('tpl')
template_id = settings.TENCENT_SMS_TEMPLATE.get(tpl)
if not template_id:
# self.add_error('短信模板错误')
raise ValidationError('短信模板错误')
# 校验数据库是否存在
exists = models.UserInfo.objects.filter(mobile_phone=mobile_phone).exists()
if exists:
# self.add_error('手机号已存在')
raise ValidationError('手机号已存在')
# 发短信
code = random.randrange(1000, 9999)
sms = send_sms_single(mobile_phone, template_id, [code, ])
if sms['result'] != 0:
raise ValidationError('短信发送失败,{}'.format(sms['errmsg']))
# 验证码redis(django-redis)
conn = get_redis_connection()
conn.set(mobile_phone, code, ex=60)
return mobile_phone
class LoginSMSForm(forms.Form):
mobile_phone = forms.CharField(label='手机号', validators=[RegexValidator(r'(1[3|4|5|6|7|8|9])\d{9}$', '手机号格式错误'), ])
code = forms.CharField(label='验证码', widget=forms.TextInput())
def __init__(self, *args, **kwargs):
"""
重写RegisterModelForm 的 初始化方法
name 表示字段名称
field 表示forms.CharField对象
code = forms.CharField(label='验证码', widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': "请输入验证码"}))
"""
super(LoginSMSForm, self).__init__(*args, **kwargs)
for name, field in self.fields.items():
field.widget.attrs['class'] = 'form-control'
field.widget.attrs['placeholder'] = "请输入{}".format(field.label)
- 第三步 :短信登录页面视图
(Bug_manager) daizhe@daizhedeMacBook-Pro Bug_manager % cat web_app/templates/login_sms.html
{% extends 'layout/basic.html' %}
{% load static %}
{% block title %} 用户短信登录 {% endblock %}
{% 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">用户短信登录</div>
<form id="smsForm" 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="点击获取验证码">
</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>#}
{# <div style="float: right;">#}
{# <a href="{% url 'login' %}">用户名密码登录?</a>#}
{# </div>#}
{# </div>#}
<div class="row">
<div class="col-xs-3">
<input id="btnSubmit" type="button" class="btn btn-primary" value="登 录"/>
</div>
</div>
</form>
</div>
{% endblock %}
{% block js %}
<script>
// 页面框架加载完成之后自动执行函数
$(function () {
bindClickBtnSms();
bindClickSubmit();
});
/*
点击登录
*/
function bindClickSubmit() {
$('#btnSubmit').click(function () {
$('.error-msg').empty();
// 收集表单中的数据(找到每一个字段)$('#regForm').serialize()
// 数据ajax发送到后台
$.ajax({
url: "{% url 'login_sms' %}",
type: "POST",
data: $('#smsForm').serialize(), // 所有字段数据 + 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]);
})
}
}
})
})
}
/*
点击获取验证码的按钮绑定事件
*/
function bindClickBtnSms() {
$('#btnSms').click(function () {
$('.error-msg').empty();
// 获取用户输入的手机号
// 找到输入框的ID,根据ID获取值,如何找到手机号的那个ID?
var mobilePhone = $('#id_mobile_phone').val();
// 发送ajax请求,把手机号发送过去
$.ajax({
url: "{% url 'send_sms' %}", // 等价于 /send/sms/
type: "GET",
data: {mobile_phone: mobilePhone, tpl: "login"},
dataType: "JSON", // 将服务端返回的数据反序列化为字典
success: function (res) {
// ajax请求发送成功之后,自动执行的函数; res就是后端返回的值
if (res.status) {
sendSmsRemind();
} else {
// 错误信息
// console.log(res); // {status:False, error:{ mobile_phone: ["错误信息",],code: ["错误信息",] } }
$.each(res.error, function (key, value) {
$("#id_" + key).next().text(value[0]);
})
}
}
})
})
}
/*
倒计时
*/
function sendSmsRemind() {
var $smsBtn = $('#btnSms');
$smsBtn.prop('disabled', true); // 禁用
var time = 60;
var remind = setInterval(function () {
$smsBtn.val(time + '秒重新发送');
time = time - 1;
if (time < 1) {
clearInterval(remind);
$smsBtn.val('点击获取验证码').prop('disabled', false);
}
}, 1000)
}
</script>
{% endblock %}
- 查看页面效果
- 第四步 :优化
Form
中注册及短信登录的__init__
中BootStrap
中共有的前端样式,编写成一个基类,其他用到 BootStrap样式的Form也继承此基类;
# 定义BootStrap基类
(Bug_manager) daizhe@daizhedeMacBook-Pro Bug_manager % cat web_app/forms/bootstrap.py
class BootStrapForm(object):
def __init__(self, *args, **kwargs):
"""
重写RegisterModelForm 的 初始化方法
name 表示字段名称
field 表示forms.CharField对象
code = forms.CharField(label='验证码', widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': "请输入验证码"}))
"""
super().__init__(*args, **kwargs)
for name, field in self.fields.items():
field.widget.attrs['class'] = 'form-control'
field.widget.attrs['placeholder'] = "请输入{}".format(field.label)
# Form 中 用到 BootStrap 属性的就继承BootStrap基类
(Bug_manager) daizhe@daizhedeMacBook-Pro Bug_manager % cat web_app/forms/account.py
from django import forms
from web_app import models
from django.core.validators import RegexValidator
from django.core.exceptions import ValidationError
from django.conf import settings
from django_redis import get_redis_connection
import random
from utils.tencent.sms import send_sms_single
from web_app.forms.bootstrap import BootStrapForm
class RegisterModelForm(BootStrapForm, forms.ModelForm):
password = forms.CharField(label='密码',
min_length=8,
max_length=64,
error_messages={
"min_length": "密码长度不能小于8个字符",
"max_length": "密码长度不能大于64个字符",
},
widget=forms.PasswordInput())
confirm_password = forms.CharField(label='重复密码', widget=forms.PasswordInput())
mobile_phone = forms.CharField(label='手机号', validators=[RegexValidator(r'(1[3|4|5|6|7|8|9])\d{9}$', '手机号格式错误'), ])
code = forms.CharField(label='验证码', widget=forms.TextInput())
# model 定义元数据
class Meta:
# 对应的Model类
model = models.UserInfo
# Model类中哪些字段可以展示,__all__ 表示所有
# fields = '__all__' 也表示默认的展示顺序,可以手动指定展示顺序
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('用户名已存在')
return username
def clean_email(self):
"""
邮箱校验的钩子函数
"""
email = self.cleaned_data['email']
exists = models.UserInfo.objects.filter(email=email).exists()
if exists:
raise ValidationError('邮箱已存在')
return email
def clean_confirm_password(self):
"""
密码和确认密码对比校验
"""
pwd = self.cleaned_data['password']
confirm_pwd = self.cleaned_data['confirm_password']
if pwd != confirm_pwd:
raise ValidationError('两次密码不一致')
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('手机号已注册')
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('验证码失效或未发送,请重新发送')
redis_str_code = redis_code.decode('utf-8')
if code.strip() != redis_str_code:
raise ValidationError('验证码错误,请重新输入')
return code
class SendSmsForm(forms.Form):
mobile_phone = forms.CharField(label='手机号', validators=[RegexValidator(r'(1[3|4|5|6|7|8|9])\d{9}$', '手机号格式错误'),])
# 重写__init__ 仅为在views视图调用Form初始化函数式多传递request参数
def __init__(self, request, *args, **kwargs):
super(SendSmsForm, self).__init__(*args, **kwargs)
self.request = request
def clean_mobile_phone(self):
"""手机号校验钩子函数 (单个字段方法如 def cleand_字段名())"""
mobile_phone = self.cleaned_data['mobile_phone']
# 判断短信模板是否有问题
tpl = self.request.GET.get('tpl')
template_id = settings.TENCENT_SMS_TEMPLATE.get(tpl)
if not template_id:
# self.add_error('短信模板错误')
raise ValidationError('短信模板错误')
# 校验数据库是否存在
exists = models.UserInfo.objects.filter(mobile_phone=mobile_phone).exists()
if exists:
# self.add_error('手机号已存在')
raise ValidationError('手机号已存在')
# 发短信
code = random.randrange(1000, 9999)
sms = send_sms_single(mobile_phone, template_id, [code, ])
if sms['result'] != 0:
raise ValidationError('短信发送失败,{}'.format(sms['errmsg']))
# 验证码redis(django-redis)
conn = get_redis_connection()
conn.set(mobile_phone, code, ex=60)
return mobile_phone
class LoginSMSForm(BootStrapForm, forms.Form):
mobile_phone = forms.CharField(label='手机号', validators=[RegexValidator(r'(1[3|4|5|6|7|8|9])\d{9}$', '手机号格式错误'), ])
code = forms.CharField(label='验证码', widget=forms.TextInput())
1.2、点击发送短信
- 第一步 :短信登录页面,点击获取验证码前端绑定事件,将前端的信息发送到后端;
- 注意的是登录的短信验证码码模板
tlp=login
- 注意的是登录的短信验证码码模板
{% block js %}
<script>
// 页面框架加载完成之后自动执行函数
$(function () {
bindClickBtnSms();
bindClickSubmit();
});
/*
点击登录
*/
function bindClickSubmit() {
$('#btnSubmit').click(function () {
$('.error-msg').empty();
// 收集表单中的数据(找到每一个字段)$('#regForm').serialize()
// 数据ajax发送到后台
$.ajax({
url: "{% url 'login_sms' %}",
type: "POST",
data: $('#smsForm').serialize(), // 所有字段数据 + 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]);
})
}
}
})
})
}
/*
点击获取验证码的按钮绑定事件
*/
function bindClickBtnSms() {
$('#btnSms').click(function () {
$('.error-msg').empty();
// 获取用户输入的手机号
// 找到输入框的ID,根据ID获取值,如何找到手机号的那个ID?
var mobilePhone = $('#id_mobile_phone').val();
// 发送ajax请求,把手机号发送过去
$.ajax({
url: "{% url 'send_sms' %}", // 等价于 /send/sms/
type: "GET",
data: {mobile_phone: mobilePhone, tpl: "login"},
dataType: "JSON", // 将服务端返回的数据反序列化为字典
success: function (res) {
// ajax请求发送成功之后,自动执行的函数; res就是后端返回的值
if (res.status) {
sendSmsRemind();
} else {
// 错误信息
// console.log(res); // {status:False, error:{ mobile_phone: ["错误信息",],code: ["错误信息",] } }
$.each(res.error, function (key, value) {
$("#id_" + key).next().text(value[0]);
})
}
}
})
})
}
/*
倒计时
*/
function sendSmsRemind() {
var $smsBtn = $('#btnSms');
$smsBtn.prop('disabled', true); // 禁用
var time = 60;
var remind = setInterval(function () {
$smsBtn.val(time + '秒重新发送');
time = time - 1;
if (time < 1) {
clearInterval(remind);
$smsBtn.val('点击获取验证码').prop('disabled', false);
}
}, 1000)
}
</script>
{% endblock %}
- 第二步 :数据校验,当使用短信验证码登录时,则不需要像此前注册 Form 钩子函数中手机号校验此前必须没有,短信登录而是需要校验数据库中已经存在此手机号,表示此前已经注册过;
(Bug_manager) daizhe@daizhedeMacBook-Pro Bug_manager % cat web_app/forms/account.py
...
...
class SendSmsForm(forms.Form):
mobile_phone = forms.CharField(label='手机号', validators=[RegexValidator(r'(1[3|4|5|6|7|8|9])\d{9}$', '手机号格式错误'),])
# 重写__init__ 仅为在views视图调用Form初始化函数式多传递request参数
def __init__(self, request, *args, **kwargs):
super(SendSmsForm, self).__init__(*args, **kwargs)
self.request = request
def clean_mobile_phone(self):
"""手机号校验钩子函数 (单个字段方法如 def cleand_字段名())"""
mobile_phone = self.cleaned_data['mobile_phone']
# 判断短信模板是否有问题
tpl = self.request.GET.get('tpl')
template_id = settings.TENCENT_SMS_TEMPLATE.get(tpl)
if not template_id:
# self.add_error('短信模板错误')
raise ValidationError('短信模板错误')
# 校验数据库是否存在
exists = models.UserInfo.objects.filter(mobile_phone=mobile_phone).exists()
if tpl == 'login':
if not exists:
# self.add_error('手机号已存在')
raise ValidationError('手机号不存在')
else:
# 校验手机号是否存在
if exists:
raise ValidationError('手机号已存在')
# 发短信
code = random.randrange(1000, 9999)
sms = send_sms_single(mobile_phone, template_id, [code, ])
if sms['result'] != 0:
raise ValidationError('短信发送失败,{}'.format(sms['errmsg']))
# 验证码redis(django-redis)
conn = get_redis_connection()
conn.set(mobile_phone, code, ex=60)
return mobile_phone
...
...
1.3、点击登录
- 第一步 :前端 将登陆按钮绑定事件,获取表单数据发送到后台;
(Bug_manager) daizhe@daizhedeMacBook-Pro Bug_manager % cat web_app/templates/login_sms.html
...
...
<div class="row">
<div class="col-xs-3">
<input id="btnSubmit" type="button" class="btn btn-primary" value="登 录"/>
</div>
</div>
</form>
</div>
{% endblock %}
{% block js %}
<script>
// 页面框架加载完成之后自动执行函数
$(function () {
bindClickBtnSms();
bindClickSubmit();
});
/*
点击登录
*/
function bindClickSubmit() {
$('#btnSubmit').click(function () {
$('.error-msg').empty();
// 收集表单中的数据(找到每一个字段)$('#regForm').serialize()
// 数据ajax发送到后台
$.ajax({
url: "{% url 'login_sms' %}", // http://localhost/web_apps/login/sms
type: "POST",
data: $('#smsForm').serialize(), // 所有字段数据 + 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]);
})
}
}
})
})
}
...
...
- 第二步 :后端视图接收到POST请求后进行表单校验
- 方式一 (代码使用):
# Form
class LoginSMSForm(BootStrapForm, forms.Form):
mobile_phone = forms.CharField(label='手机号', validators=[RegexValidator(r'(1[3|4|5|6|7|8|9])\d{9}$', '手机号格式错误'), ])
code = forms.CharField(label='验证码', widget=forms.TextInput())
def clean_mobile_phone(self):
"""手机号校验钩子函数"""
mobile_phone = self.cleaned_data['mobile_phone']
exists = models.UserInfo.objects.filter(mobile_phone=mobile_phone).exists()
if not exists:
raise ValidationError("手机号不存在")
def clean_code(self):
"""验证码校验钩子函数"""
code = self.cleaned_data['code']
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('验证码失效或未发送,请重新发送')
redis_str_code = redis_code.decode('utf-8')
if code.strip() != redis_str_code:
raise ValidationError('验证码错误,请重新输入')
return code
- 方式二 :
(Bug_manager) daizhe@daizhedeMacBook-Pro Bug_manager % cat web_app/forms/account.py
...
...
class LoginSMSForm(BootStrapForm, forms.Form):
mobile_phone = forms.CharField(label='手机号', validators=[RegexValidator(r'(1[3|4|5|6|7|8|9])\d{9}$', '手机号格式错误'), ])
code = forms.CharField(label='验证码', widget=forms.TextInput())
def clean_mobile_phone(self):
"""手机号校验钩子函数"""
mobile_phone = self.cleaned_data['mobile_phone']
user_objects = models.UserInfo.objects.filter(mobile_phone=mobile_phone).first()
if not user_objects:
raise ValidationError("手机号不存在")
return user_objects # 返回用户对象
def clean_code(self):
"""验证码校验钩子函数"""
code = self.cleaned_data['code']
# mobile_phone = self.cleaned_data.get('mobile_phone')
user_objects = self.cleaned_data.get('mobile_phone')
# 如果没有手机号则不进行校验
if not user_objects:
return code
conn = get_redis_connection()
redis_code = conn.get(user_objects.mobile_phone)
if not redis_code:
raise ValidationError('验证码失效或未发送,请重新发送')
redis_str_code = redis_code.decode('utf-8')
if code.strip() != redis_str_code:
raise ValidationError('验证码错误,请重新输入')
return code
...
...
# views视图
(Bug_manager) daizhe@daizhedeMacBook-Pro Bug_manager % cat web_app/views/account.py
...
...
def send_sms(request):
# 校验方法一
# mobile_phone = request.GET.get('mobile_phone')
# tpl = request.GET.get('tpl')
# 校验方法二 将前端数据数据使用Form校验,SendSmsForm中定义的仅校验手机号
form = SendSmsForm(request, data=request.GET)
# is_valid 只是校验手机号,不能为空、格式正确
if form.is_valid():
return JsonResponse({'status': True})
return JsonResponse({'status': False, 'error': form.errors })
def login_sms(request):
""" 短信登录 """
if request.method == 'GET':
form = LoginSMSForm()
return render(request, 'login_sms.html', {'form': form})
form = LoginSMSForm(request.POST)
if form.is_valid():
# 用户输入正确,登录成功
user_objects = form.cleaned_data['mobile_phone']
return JsonResponse({'status': True, 'data': "/index/"})
return JsonResponse({"status": False, 'error': form.errors})
...
...
向往的地方很远,喜欢的东西很贵,这就是我努力的目标。