當輸入手機號時需要檢測該手機號是否注冊過,然後點擊登錄時一起將手機號和驗證嗎提交給後端,再次校驗手機號和驗證碼。
這裡使用form的方式將字段渲染再前端,在form中校驗字段。
首先將字段傳入到模板中:
views.py:
def login_sms(request):
if request.method == 'GET':
form = LoginSmsForm(request.GET)
return render(request, 'user/login_sms.html', dict(form=form))
login.html:
<div class="container">
<div class="row">
<div class="col-md-4 col-md-offset-4">
<div class="account">
<h2 class="text-center">短信登錄</h2>
<form id="form_data">
{% csrf_token %}
{% for field in form %}
{% if field.label == '驗證碼' %}
<div class="form-group">
<label for="{
{ field.id_for_label }}">{
{ field.label }}</label>
<div class="row">
<div class="col-xs-6">
{
{ field }}
<span class="error_msg"></span>
</div>
<div class="col-xs-6">
<button id="get_code" type="button" class="btn btn-default">點擊獲取驗證碼</button>
</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>
<button type="button" id="login_sms" class="btn btn-primary">登 錄</button>
<div class="pull-right">
<a href="{% url 'login' %}">用戶登錄>>></a>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
點擊獲取驗證碼,發送ajax請求:
// 綁定點擊獲取驗證碼的事件
function bindGetCodeEvent(){
$('#get_code').click(function () {
var phoneEle = $('#id_phone');
var phone_numbe = phoneEle.val().trim();
var phone_reg = /^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$/
if (phone_numbe && phone_reg.test(phone_numbe)){
phoneEle.next().text('');
// 校驗通過之後 發送 ajax 請求
$.ajax({
url:'{% url "sms_code" %}',
type:'get',
// tpl:短信模板
data:{phone:phone_numbe, tpl:"login"},
success:function (res){
if (res.status){
//開啟倒計時效果
SmsTimer();
}else{
phoneEle.next().text(res.error_msg.phone);
}
}
})
}else {
phoneEle.next().text('手機號格式不對!');
return false
}
})
}
倒計時功能:
// 倒計時效果函數
function SmsTimer() {
// 將標簽設置為不可點擊的
// jquery 的變量一般以 $ 開頭
var $btnEle = $('#get_code');
$btnEle.prop('disable',true);
// 設置定時器讀秒效果和標簽文本內容的修改
var timer = 60;
var t = setInterval(
function () {
$btnEle.text(`${timer}秒後重新發送`);
timer--;
// 如果小於0就將定時器清除並且將$btnEle設置為可操作
if (timer <= 0){
clearInterval(t);
$btnEle.text('點擊獲取驗證碼');
$btnEle.prop('disable',false);
}
},
1000 // 1秒中執行一次這個函數
)
}
發送登錄的數據:
// 發送登錄數據
function sendLoginData() {
// 獲取用戶輸入的所有數據
// 將數據發送到後端
// 根據響應結果進行一些頁面效果處理
// 綁定點擊事件
$('#login_sms').click(function () {
var data = $('#form_data').serialize(); //拿到form表單中的所有數據
$.ajax({
url: '{% url 'login_sms' %}',
type: 'post',
data: data,
success:function (res) {
if (res.status){
location.href = res.path;
}else{
$.each(res.error_msg,function (k,v){
$('#id_' + k).next().text(v)
})
}
}
})
})
}
整體數據(手機號、驗證碼)提交時的數據校驗:
class LoginSmsForm(BootStrapForm, forms.Form):
phone = forms.CharField(label='手機號', validators=[mobile_validate, ])
sms_code = forms.CharField(label='驗證碼')
def clean_phone(self):
'''手機號唯一性校驗'''
phone = self.cleaned_data.get('phone')
phone_require = models.UserInfo.objects.filter(phone=phone)
if not phone_require.exists():
raise ValidationError('該手機號還沒注冊,請先注冊!!')
return phone
def clean_sms_code(self):
'''驗證碼校驗 * 取出 sms_code、phone * 獲取連接,從 redis 中取出 驗證碼 * 如果不同則拋出錯誤 注意點:一般局部鉤子只獲取當前局部變量,無法獲取其他變量,如在phone中不能獲取email的對象 解決方式: phone 必須在 sms_code 校驗的前面,順序就是fields中指定的。 '''
sms_code = self.cleaned_data.get('sms_code')
phone = self.cleaned_data.get('phone')
conn = get_redis_connection('sms_code')
code = conn.get(phone)
if code is not None:
if code.decode('utf-8') != sms_code.strip():
raise ValidationError('驗證碼錯誤!!!')
else:
raise ValidationError('驗證碼無效或已經超時!!')
return code
發送驗證碼時的數據校驗:
class SendSmsForm(forms.Form):
phone = forms.CharField(label='手機號', validators=[mobile_validate, ])
# 重寫 init 方法接受額外參數(request)
def __init__(self, request, *args, **kwargs):
super().__init__(*args, **kwargs)
self.request = request
def clean_phone(self):
'''手機號和短信模板校驗'''
tpl = self.request.GET.get('tpl')
try:
sms_template_id = settings.SMS_TEMPLATE_ID[tpl]
except:
raise ValidationError('短信模板錯誤!!')
phone = self.cleaned_data.get('phone')
unique_phone = models.UserInfo.objects.filter(phone=phone)
# 獲取短信驗證碼時需要區分一下是注冊還是登錄
if tpl == settings.SMS_TEMPLATE_ID.get('register'):
if unique_phone.exists():
raise ValidationError('該手機號已經注冊過了!')
elif tpl == settings.SMS_TEMPLATE_ID.get('login'):
if not unique_phone.exists():
raise ValidationError('該手機號還未注冊過!')
elif tpl == settings.SMS_TEMPLATE_ID.get('repassword'):
# TODO 邏輯待定
pass
else:
raise ValidationError(f'短信模板:{tpl}不存在!')
# 發送並校驗驗證碼
# 校驗該手機號是不是已經發送過短信了,是不是在有效期內
conn = get_redis_connection('sms_code')
if conn.get(phone) is not None:
raise ValidationError('已經發送過短信!!')
# 生成驗證碼
sms_code = '%06d' % random.randint(1, 999999)
# 將驗證碼保存到 redis 中
conn.set(phone, sms_code, ex=settings.SMS_CODE_EXPIRE)
# 發送短信
obj = MySmsSender()
obj.send(phone, sms_template_id, sms_code)
return phone
完整view試圖函數:
登錄:
def login_sms(request):
if request.method == 'GET':
form = LoginSmsForm(request.GET)
return render(request, 'user/login_sms.html', dict(form=form))
else:
form = LoginSmsForm(data=request.POST)
if form.is_valid():
# TODO 將登錄成功之後的數據保存到session中,方便後續使用
return JsonResponse(dict(status=True, path=reverse('index')))
else:
return JsonResponse(dict(status=False, error_msg=form.errors))
發送驗證碼:
def sms_code(request):
'''獲取手機號,校驗手機號,發送短信驗證碼 * 格式校驗 * 是否注冊過 '''
form = SendSmsForm(request, data=request.GET)
if form.is_valid():
return JsonResponse({
'status': True})
else:
return JsonResponse({
'status': False, 'error_msg': form.errors})