1、使用django自带功能实现登录/退出登录
使用django自带登录功能,前提生成用户(用户注册)使用的是django自带的user,或称models中用户表继承于django自带的user
1.1、登录
views.py:
from django.contrib.auth import authenticate,login,logout def acc_login(request): """登录页面""" error_msg = '' if request.method == "POST": username = request.POST.get('username') password = request.POST.get('password') user = authenticate(username = username, password = password) if user: login(request,user) return redirect(request.GET.get('next','/index/')) # 如果有next则跳转到next指定的页面,如果没有则跳转到index页面 else: error_msg = "Wrong username or password!" return render(request,"login.html",{'error_msg':error_msg})
1.2、退出登录
views.py:
def acc_logout(request): logout(request) return redirect("/login/")
urls.py:
from django.urls import path from NBCRM import views urlpatterns = [ path('login/',views.acc_login), path('logout/',views.acc_logout,name="logout"), ]
settings.py:
当某些页面设置了登录才能访问时,用户访问该页面会失败,此时可以在settings中进行下面的设置,指定用户在未登录前访问需登录才能访问的页面时,跳转到指定(登录)页面,执行某些操作,成功后才能访问目标页面。
LOGIN_URL = '/login/'
登录界面:
2.自定义登录方式
1)login相关代码:
def login(request): if request.method == "POST": username = request.POST.get("username") password = request.POST.get("password") user = models.User.objects.filter(username=username, password=password) # [User Obj, ] if user: # 登陆成功 request.session["is_login"] = "1" # request.session["username"] = username request.session["user_id"] = user[0].id # 1. 生成特殊的字符串 # 2. 特殊字符串当成key,在数据库的session表中对应一个session value # 3. 在响应中向浏览器写了一个Cookie Cookie的值就是 特殊的字符串 return redirect("/index/") return render(request, "login.html")
2)check_login:
装饰器,用于检查用户是否登录
from functools import wraps def check_login(f): @wraps(f) def inner(request, *args, **kwargs): if request.session.get("is_login") == "1": return f(request, *args, **kwargs) else: return redirect("/login/") return inner
3)index:
首页,访问前判断用户是否已登录,登录则成功访问,未登录则跳转登录页面
@check_login def index(request): user_id = request.session.get("user_id") # 根据id去数据库中查找用户 user_obj = models.User.objects.filter(id=user_id) if user_obj: return render(request, "index.html", {"user": user_obj[0]}) else: return render(request, "index.html", {"user": "匿名用户"})
2.1、session简单介绍
1)session作用
- 生成随机字符串
- 写到用户浏览器的cookie中
- 保存到session中
- 在随机字符串对应的字典中设置相关内容
2)session默认过期时间为2周,如果自己设置过期时间,自定义设置过期时间优先级高于默认时间
request.session.set_expiry(value) # 设置session过期时间
默认的过期时间是两周,如果自己设置了过期时间,这样自己设定的优先级就会高于默认的
如果value是个整数,session会在些秒数后失效。
如果value是个datatime或timedelta,session就会在这个时间后失效。
如果value是0,用户关闭浏览器session就会失效。
如果value是None,session会依赖全局session失效策略。
3)session的相关配置
# settings.py SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认) SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径(默认) SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名(默认) SESSION_COOKIE_SECURE = False # 是否Https传输cookie(默认) SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输(默认) SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周)(默认) SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过期(默认) SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改之后才保存(默认)
4)Django中对应session的存储方式
Django中支持session,其中内部提供了5种类型的session供开发者使用:
- 数据库(默认)
- 缓存
- 文件
- 缓存+数据库
- 加密cookie
1、如果是数据库,需要在settings.py中配置如下: SESSION_ENGINE = 'django.contrib.sessions.backends.db' (引擎(默认)) 2、如果是缓存session,需要在settings.py中配置如下: SESSION_ENGINE = 'django.contrib.sessions.backends.cache'(引擎) SESSION_CACHE_ALIAS= 'default' 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置 3、 如果是文件session, 需要在settings.py中配置如下: SESSION_ENGINE = 'django.contrib.sessions.backends.file' (引擎) SESSION_FILE_PATH=None 缓存文件路径,如果为None,则使用tempfile模块获取一个临时地址tempfile.gettempdir() 4、如果是缓存+数据库session,需要在settings.py中配置如下: SESSION_ENGINE='django.contrib.sessions.backends.cached_db' (引擎)
3、利用django自带登录功能,实现自定义用户名或邮箱登录,及用户注册(邮箱激活)、忘记密码等功能
3.1 实现用户注册功能
3.1.1 验证码实现 --第三方库:Captcha
1)安装captcha
pip install django-simple-captcha
2)在我们项目的setting.py中的INSTALLED_APPS注册captcha。需要注意的是,在INSTALLED_APPS中注册的,都需要经过makemigrations、migrate生成对应的表数据
INSTALLED_APPS = [ …… 'captcha', …… ]
3)配置urls.py
from django.urls import path,include,re_path urlpatterns = [ # 验证码 re_path(r'^captcha', include('captcha.urls')), ]
通过上述几步,我们就能在项目中正常使用captcha了。用法很简单:
1)后台用到验证码的地方:
from captcha.fields import CaptchaField class RegisterForm(forms.Form): """注册表单验证""" captcha = CaptchaField(error_messages={'invalid':'验证码错误'})
2)前端页面展示 --关于验证码:
<label>验 证 码</label>
{{ register_form.captcha }}
使用captcha实现验证码的好处:不需要我们判断前端验证码输入与原验证码是否一致(内部已实现了判断),只需要按上述几个步骤操作,即可实现注册/登录页面的验证码验证功能
3.1.2 实现注册功能
结合第三方库captcha,实现附有验证码的注册功能
1)注册功能 --form表单验证
from django import forms from django.core.exceptions import ValidationError from captcha.fields import CaptchaField class RegisterForm(forms.Form): """注册表单验证""" email = forms.EmailField(required=True) password = forms.CharField(required=True,min_length=6) # 验证码 内部已经包含判断验证码是否正确,不需要进行下述的验证码验证代码编写 captcha = CaptchaField(error_messages={'invalid':'验证码错误'}) # 验证码验证,使用第三方库captcha时不需要再判断,属于画蛇添足 # def clean_captcha(self): # print("captcha:",self.request.session.get('code')) # print("valid_code:",self.cleaned_data.get('code')) # if self.request.session.get('code').upper() == self.cleaned_data.get('code').upper(): # return self.cleaned_data['code'] # else: # raise ValidationError('验证码不正确') # 自定义方法(局部钩子),密码必须包含字母和数字 def clean_password(self): if self.cleaned_data.get('password').isdigit() or self.cleaned_data.get('password').isalpha(): raise ValidationError('密码必须包含数字和字母') else: return self.cleaned_data['password'] # 自定义方法(全局钩子, 检验两个字段),可用于检验两次密码是否一致; # def clean(self): # if self.cleaned_data.get('password') != self.cleaned_data.get('password2'): # raise ValidationError('密码不一致') # else: # return self.cleaned_data
2)views.py:
def register(request): """用户注册 """ if request.method == "POST": register_form = forms.RegisterForm(request.POST) if register_form.is_valid(): user_name = request.POST.get('email',None) if UserProfile.objects.filter(email=user_name): # 用户已经存在,不需要再注册 return render(request,'register.html',{'msg':'用户已经存在','register_form':register_form}) pass_word = request.POST.get('password', None) # 将密码加密后再保存 pass_word = make_password(pass_word) UserProfile.objects.create( username=user_name, email=user_name, is_active=False, password=pass_word ) email_send_status = email_send.send_register_email(user_name, 'register') # 发送邮件,用于用户激活账号 if email_send_status: # 状态为1,表示邮件发送成功 email_send_success = True # 用于前端判断发送邮件的类型 return render(request,'send_email_success.html',locals()) else: # status_form = True #用于前端注册时判断是否填充客户输入原数据 return render(request, 'register.html', {'msg':'用户名或密码格式错误','register_form':register_form}) else: register_form = forms.RegisterForm() return render(request,'register.html',locals())
3)urls.py配置:
from django.urls import path from web_online import views urlpatterns = [ path('register/',views.register, name='register'), # 验证码url re_path(r'^captcha',include('captcha.urls')), ]
4)HTML前端代码(仅供参考):
<!DOCTYPE html> {% load staticfiles %} <html> <head> <meta charset="UTF-8"> <meta name="renderer" content="webkit"> <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1" > <title>慕学在线注册</title> <link rel="stylesheet" type="text/css" href="{% static 'css/reset.css' %}"> <link rel="stylesheet" type="text/css" href="{% static 'css/login.css' %}"> </head> <body> <div class="loginbox dialogbox"> <h1>账号登录</h1> <div class="close jsCloseDialog"><img src="{% static 'images/dig_close.png' %}"/></div> <div class="cont"> <form id="jsLoginForm" autocomplete="off"> <div class="box"> <span class="word3">用户名</span> <input type="text" name="account_l" id="account_l" placeholder="手机号/邮箱" /> </div> <div class="box"> <span class="word2">密 码</span> <input type="password" name="password_l" id="password_l" placeholder="请输入您的密码"/> </div> <div class="error btns login-form-tips" id="jsLoginTips"></div> <!--登录错误提示--> <div class="btns"> <span><input type="checkbox" id="jsAutoLogin" /> 自动登录</span> <span class="forget btn fr">忘记密码</span> </div> <div class="button"> <input type="button" value="登录" id="jsLoginBtn" /> </div> <div class="btns"> <span class="fr">没有账号?<span class="regist btn">立即注册</span></span> </div> </form> </div> </div> <div class="dialog" id="jsDialog"> <!--提示弹出框--> <div class="successbox dialogbox" id="jsSuccessTips"> <h1>成功提交</h1> <div class="close jsCloseDialog"><img src="{% static 'images/dig_close.png' %}"/></div> <div class="cont"> <h2>您的需求提交成功!</h2> <p></p> </div> </div> <div class="noactivebox dialogbox" id="jsUnactiveForm" > <h1>邮件验证提示</h1> <div class="close jsCloseDialog"><img src="{% static 'images/dig_close.png' %}"/></div> <div class="center"> <img src="{% static 'images/send.png' %}"/> <p>我们已经向您的邮箱<span class="green" id="jsEmailToActive">12@13.com</span>发送了邮件,<br/>为保证您的账号安全,请及时验证邮箱</p> <p class="a"><a class="btn" id="jsGoToEmail" target="_blank" href="http://mail.qq.com">去邮箱验证</a></p> <p class="zy_success upmove"></p> <p style="display: none;" class="sendE2">没收到,您可以查看您的垃圾邮件和被过滤邮件,也可以再次发送验证邮件(<span class="c5c">60s</span>)</p> <p class="sendE">没收到,您可以查看您的垃圾邮件和被过滤邮件,<br/>也可以<span class="c5c green" id="jsSenEmailAgin" style="cursor: pointer;">再次发送验证邮件</span></p> </div> </div> </div> <div class="bg" id="dialogBg"></div> <header> <div class="c-box fff-box"> <div class="wp header-box"> <p class="fl hd-tips">慕学在线,在线学习平台!</p> <ul class="fr hd-bar"> <li>服务电话:<span>4001008031</span></li> <li><a href="{% url 'login' %}">[登录]</a></li> <li class="active"><a href="{% url 'register' %}">[注册]</a></li> </ul> </div> </div> </header> <section> <div class="c-box bg-box"> <div class="login-box clearfix"> <div class="hd-login clearfix"> <a class="index-logo" href="/index/"></a> <h1>用户注册</h1> <a class="index-font" href="/index/">回到首页</a> </div> <div class="fl slide"> <div class="imgslide"> <ul class="imgs"> <li><a href=""><img width="483" height="472" src="{% static 'images/57a801860001c34b12000460.jpg' %}" /></a></li> <li><a href=""><img width="483" height="472" src="{% static 'images/57a801860001c34b12000460.jpg' %}" /></a></li> <li><a href=""><img width="483" height="472" src="{% static 'images/57a801860001c34b12000460.jpg' %}" /></a></li> </ul> </div> <div class="unslider-arrow prev"></div> <div class="unslider-arrow next"></div> </div> <div class="fl form-box"> <div class="tab"> <!--<h2 class="active">手机注册</h2>--> <h2>邮箱注册</h2> </div> <div class="tab-form"> <form id="email_register_form" method="post" action="{% url 'register' %}" autocomplete="off"> {% csrf_token %} <div class="form-group marb20 {% if register_form.errors.email.0 %} errorput {% endif %}"> <label>邮 箱</label> <input type="text" id="id_email" name="email" {% if register_form.email.value %} value="{{ register_form.email.value }}" {% endif %} placeholder="请输入您的邮箱地址" /> </div> <div class="error btns" id="jsEmailTips">{{ register_form.errors.email.0 }}</div> <div class="form-group marb8 {% if register_form.errors.password.0 %} errorput {% endif %}"> <label>密 码</label> <input type="password" id="id_password" name="password" {% if register_form.password.value %} value="{{ register_form.password.value }}" {% endif %} placeholder="请输入6-20位非中文字符密码" /> </div> <div class="error btns" id="jsEmailTips">{{ register_form.errors.password.0 }}</div> <div class="form-group marb8 captcha1 {% if register_form.errors.captchal.0 %} errorput {% endif %}"> {# <label>验 证 码</label>#} {# {{ register_form.captcha }}#} <input name='code' type="text" placeholder="验证码" /> {{ register_form.captcha }} </div> <div class="error btns" id="jsEmailTips">{{ register_form.errors.captcha.0 }}</div> <div class="error btns" id="jsEmailTips"> {{ msg }} </div> <div class="auto-box marb8"> </div> <input class="btn btn-green" id="jsEmailRegBtn" type="submit" value="注册" /> </form> </div> <p class="form-p">已有账号?<a href="{% url 'login' %}">[立即登录]</a></p> </div> </div> </div> </section> <input id="isLogin" type="hidden" value="False"/> <script src="{% static 'js/jquery.min.js' %}" type="text/javascript"></script> <script src="{% static 'js/unslider.js' %}" type="text/javascript"></script> <script src="{% static 'js/validateDialog.js' %}" type="text/javascript"></script> <script src="{% static 'js/login.js' %}" type="text/javascript"></script> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {% if email_send_success %} <p>【注册账号激活】邮件已发送,请移步邮箱中查收!</p> {% else %} <p>【密码重置】邮件已发送,请移步邮箱中查收!</p> {% endif %} </body> </html>
3.1.3 注册账号激活
在上述注册功能实现过程中涉及到邮件激活账号:email_send.send_register_email(user_name,'register') , 下面实现注册账号激活功能
注意:用户账号激活时,本质是通过is_active=True来实现的,如果将is_staff也设置为True,则表示该用户可以登录我们项目的django后台。切记,只有is_active、is_staff同时为True,才能登录django后台。
1)email_send.py:用于给用户发送邮件,实现下述功能
- 账号激活
- 忘记密码,重置密码
- 修改密码
import random from users.models import EmailVerifyRecord from django.core.mail import send_mail from web_online import settings def random_str(random_length=16): """默认生成16位随机字符串""" str = '' # 生成字符串的可选字符串 chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789' length = len(chars) - 1 # ran_dom = random.Random() for i in range(random_length): str += chars[random.randint(0, length)] return str # 发送邮件 def send_register_email(email, send_type="register"): """ 发送邮件 发送前将当前注册用户保存到数据库,方便后台激活账号时能在数据库中找到对应的注册用户 """ if send_type == 'update_email': # 修改密码操作 code = random_str(4) else: code = random_str(16) # 保存到数据库 EmailVerifyRecord.objects.create( code=code, email=email, send_type=send_type ) # 定义邮箱内容: if send_type == "register": # 注册激活账号 subject = "Mx在线教育注册激活链接" # 标题 email_body = "请复制打开下面的链接激活你的账号:http://127.0.0.1:8000/active/{0}".format(code) # 文本邮件体 sender = settings.DEFAULT_FROM_EMAIL # 发件人 receiver = [email] # 接收人 email_send_status = send_mail(subject, email_body, sender, receiver) return email_send_status # if email_send_status: # pass elif send_type == 'forget': # 忘记密码 重置密码 subject = "Mx在线教育重置密码链接" # 标题 email_body = "请复制打开下面的链接重置密码:http://127.0.0.1:8000/reset/{0}".format(code) # 文本邮件体 sender = settings.DEFAULT_FROM_EMAIL # 发件人 receiver = [email] # 接收人 email_send_status = send_mail(subject, email_body, sender, receiver) return email_send_status elif send_type == "update_email": # 修改密码验证码 subject = "Mx在线教育邮箱修改验证码" email_body = "你的邮箱验证码为{0}".format(code) sender = settings.DEFAULT_FROM_EMAIL receiver = [email] # 使用Django内置函数完成邮件发送。四个参数:主题,邮件内容,从哪里发,接受者list send_status = send_mail(subject, email_body, sender, receiver) # 如果发送成功 if send_status: pass
2)使用django自带的邮件发送功能,需要在settings.py中配置发送邮件的基本数据:
# 邮箱配置 EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' EMAIL_HOST = "smtp.163.com" #以163邮箱为例,SMTP服务器(邮箱需要开通SMTP服务) EMAIL_HOST_PASSWORD = '******' #SMTP服务授权码 DEFAULT_FROM_EMAIL = EMAIL_HOST_USER = "13*******@163.com" #我的163邮箱帐号 EMAIL_PORT = 25 #163邮箱SMTP服务端口 EMAIL_USE_TLS = True # 163、qq邮箱此值为True,aliyun此值为False,163可以忽略此值 # EMAIL_SUBJECT_PREFIX = '[yshblog.com]' #邮件标题前缀,默认是'[django]'
3)views.py:
注册账号激活功能
def user_active(request, accode): """注册用户账号激活""" if request.method == "GET": ac_records = EmailVerifyRecord.objects.filter(code=accode) if ac_records: # 有当前注册用户 five_mintes_ago = datetime.now() - timedelta(hours=0, minutes=5, seconds=0) ac_record = ac_records[0] # print("five_mintes_ago:",five_mintes_ago) # print("send_time:",ac_record.send_time) if five_mintes_ago > ac_record.send_time: # 发送时间超过5分钟,返回链接失效页面 return render(request, 'active_fail.html') ac_email = ac_record.email ac_user = UserProfile.objects.get(email=ac_email) # 当前注册用户 ac_user.is_active = True ac_user.save() return render(request,'active_success.html',locals()) else: return render(request,'active_fail.html')
4)urls.py配置:
from django.urls import path from web_online import views urlpatterns = [ re_path(r'^active/(\w+)/', views.user_active, name='user_active'), # 邮箱激活账号 ]
5)注册成功返回页面、注册用户失败或链接失效返回页面:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>mx教育邮箱注册成功</title> </head> <body> <h3>恭喜!您已成功激活账号:{{ ac_email }}</h3> <p style="color: red">点击跳转:<a href="/login/">登录</a></p> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <p style="color: red;">链接已经失效</p> </body> </html>
至此,用户注册及激活功能即完成。
3.2 实现自定义用户名或邮箱登录功能
3.2.1 userProfile继承于 AbstractUser
需要注意的是:AbstractUser中定义的email字段不具有唯一性,容易造成多个用户使用同一邮箱,当使用邮箱登录时会造成冲突。因此,需要重写email字段,使其与username字段一样,具备 '唯一'性
from django.db import models from django.contrib.auth.models import AbstractUser class UserProfile(AbstractUser): """用户""" gender_choices = ( ('male','男'), ('female','女') ) nick_name = models.CharField('昵称',max_length=32 ,default='') birthday = models.DateField('生日',null=True,blank=True) gender = models.CharField('性别',max_length=8,choices=gender_choices,default='female') adress = models.CharField('地址',max_length=100,default='') mobile = models.CharField('手机号',max_length=11,null=True,blank=True) image = models.ImageField(upload_to='image/%Y/%m',default='image/default.png',max_length=100) email = models.EmailField('邮箱', blank=True,unique=True) # 重写email字段,加上'唯一'标识 class Meta: verbose_name = '用户信息' verbose_name_plural = verbose_name def __str__(self): return self.username
此时 userProfile表所存字段:
3.2.2 在views中编写登录功能之前,我们需要先写一个form表单,用于用户登录时,对用户名及密码格式进行验证:
forms.py/LoginForm
from django import forms class LoginForm(forms.Form): """登录表单验证""" username = forms.CharField(required=True) password = forms.CharField(required=True, min_length=6)
3.2.3 接着在views界面中编写我们的登录代码:
from django.shortcuts import render,redirect from django.contrib.auth import authenticate ,login ,logout from users.models import UserProfile from web_online import forms def mx_login(request): """登录""" login_form = forms.LoginForm() if request.method == "POST": login_form = forms.LoginForm(request.POST) if login_form.is_valid(): user_name = request.POST.get('username',None) pass_word = request.POST.get('password',None) user = authenticate(username=user_name, password=pass_word) if user: if user.is_active: # 只有注册激活才能登陆 login(request,user) return redirect('/index/') else: return render(request,'login.html',{'msg': '用户未激活', 'login_form': login_form}) else: return render(request, 'login.html', {'msg': '用户名或密码错误', 'login_form': login_form}) else: return render(request, 'login.html', {'msg': '用户名或密码格式错误,请重新输入!', 'login_form': login_form}) return render(request,"login.html")
简单解析:
1. is_active:在注册中,我们对成功激活账号的用户设置is_active为True,在登录操作中就可以通过is_active的状态来判断用户是否是注册成功的用户,来决定是否给用户登录权限
2. authenticate()方法的验证:我们使用的是django自带的authenticate验证, 默认只对用户名、密码进行验证,我们要使用用户名或邮箱进行验证,需要自定义authenticate方法,实现代码如下:
class CustomBackend(ModelBackend): """ 用于mx_login用户登录验证 需在settings中配置好authenticate验证方式(即在此进行authenticate的相关验证) """ def authenticate(self, request, username=None, password=None, **kwargs): # 重写authenticate方法 try: user = UserProfile.objects.get(Q(username=username)|Q(email=username)) if user.check_password(password): """ 1.在注册时,我们对密码使用了加密处理(django下的make_password),因此在登录验证密码时需要先将明文密码加密 后才能跟数据库中已加密后的密码进行比较 2.check_password()是AbstractUser类中的方法,UserProfile继承于AbstractUser,check_password会加密明文密 码后,与数据库密码做对比,再进行判断两密码是否一致 3.验证如果为True,表示密码一致;为False,表示密码不一致 """ return user else: return None except Exception as e: return None
以上基本实现了自定义登录功能验证,但要验证流程中的authenticate验证走我们自定义的authenticate验证,需要在settings.py中配置AUTHENTICATION_BACKENDS ,如不配置,默认会走django自带的authenticate验证:
AUTHENTICATION_BACKENDS = ( # 登录认证设置 'web_online.views.CustomBackend', #CustomBackend所在路径 )
3.2.4 url配置:
from django.urls import path from web_online import views urlpatterns = [ path('login/',views.mx_login, name='login'), ]
3.2.5 html配置:
注意:HTML前端代码未附带css、js代码,仅供参考
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="renderer" content="webkit"> <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1" > <title>慕学在线网登录</title> <link rel="stylesheet" type="text/css" href="/static/css/reset.css"> <link rel="stylesheet" type="text/css" href="/static/css/login.css"> </head> <body> <div class="dialog" id="jsDialog"> <!--提示弹出框,用于找回密码操作时的提示--> <div class="successbox dialogbox" id="jsSuccessTips"> <h1>成功提交</h1> <div class="close jsCloseDialog"><img src="/static/images/dig_close.png"/></div> <div class="cont"> <h2>您的需求提交成功!</h2> <p></p> </div> </div> <div class="noactivebox dialogbox" id="jsUnactiveForm" > <h1>邮件验证提示</h1> <div class="close jsCloseDialog"><img src="/static/images/dig_close.png"/></div> <div class="center"> <img src="/static/images/send.png"/> <p>我们已经向您的邮箱<span class="green" id="jsEmailToActive">12@13.com</span>发送了邮件,<br/>为保证您的账号安全,请及时验证邮箱</p> <p class="a"><a class="btn" id="jsGoToEmail" target="_blank" href="http://mail.qq.com">去邮箱验证</a></p> <p class="zy_success upmove"></p> <p style="display: none;" class="sendE2">没收到,您可以查看您的垃圾邮件和被过滤邮件,也可以再次发送验证邮件(<span class="c5c">60s</span>)</p> <p class="sendE">没收到,您可以查看您的垃圾邮件和被过滤邮件,<br/>也可以<span class="c5c green" id="jsSenEmailAgin" style="cursor: pointer;">再次发送验证邮件</span></p> </div> </div> </div> <div class="bg" id="dialogBg"></div> <header> <div class="c-box fff-box"> <div class="wp header-box"> <p class="fl hd-tips">慕学在线网,在线学习平台!</p> <ul class="fr hd-bar"> <li>服务电话:<span>33333333</span></li> <li class="active"><a href="{% url 'login' %}">[登录]</a></li> <li><a href="{% url 'register' %}">[注册]</a></li> </ul> </div> </div> </header> <section> <div class="c-box bg-box"> <div class="login-box clearfix"> <div class="hd-login clearfix"> <a class="index-logo" href="/index/"></a> <h1>用户登录</h1> <a class="index-font" href="/index/">回到首页</a> </div> <div class="fl slide"> <div class="imgslide"> <ul class="imgs"> <li><a href=""><img width="483" height="472" src="/static/images/mysql.jpg"/></a></li> <li><a href=""><img width="483" height="472" src="/static/images/mysql.jpg"/></a></li> <li><a href=""><img width="483" height="472" src="/static/images/mysql.jpg"/></a></li> </ul> </div> <div class="unslider-arrow prev"></div> <div class="unslider-arrow next"></div> </div> <div class="fl form-box"> <h2>帐号登录</h2> <form action="/login/" method="post" autocomplete="off"> {% csrf_token %} <div class="form-group marb20 {% if login_form.errors.username.0 %} errorput {% endif %}"> <label>用 户 名</label> <input name="username" id="account_l" type="text" placeholder="手机号/邮箱" {% if login_form.username.value %}value="{{ login_form.username.value }}"{% endif %}/> </div> <div class="error btns login-form-tips" id="jsLoginTips">{{ login_form.errors.username.0 }}</div> <div class="form-group marb8 {% if login_form.errors.password.0 %} errorput {% endif %}"> <label>密 码</label> <input name="password" id="password_l" type="password" {% if login_form.password.value %}value="{{ login_form.password.value }}"{% endif %} placeholder="请输入您的密码"/> </div> <div class="error btns login-form-tips" id="jsLoginTips">{{ login_form.errors.password.0 }}</div> <div class="error btns login-form-tips" id="jsLoginTips">{{ msg }}</div> <div class="auto-box marb38"> <a class="fr" href="#">忘记密码?</a> {# <a class="fr" href="{% url 'forgetpwd' %}">忘记密码?</a>#} </div> <input class="btn btn-green" id="jsLoginBtn" type="submit" value="立即登录 > "/> <input type='hidden' name='csrfmiddlewaretoken' value='5I2SlleZJOMUX9QbwYLUIAOshdrdpRcy'/> {% csrf_token %} </form> <p class="form-p">没有慕学在线网帐号?<a href="{% url 'register' %}">[立即注册]</a></p> </div> </div> </div> </section> <script src="/static/js/jquery.min.js" type="text/javascript"></script> <script src="/static/js/unslider.js" type="text/javascript"></script> <script src="/static/js/login.js" type="text/javascript"></script> </body> </html>
3.3 退出登录功能
3.3.1 退出登录
实现退出登录功能很简单,在views.py中编写退出登录代码:
from django.contrib.auth import logout def mx_logout(request): """退出登录""" logout(request) return redirect('/login/') # 返回登录页面
3.3.2 url配置:
from django.urls import path from web_online import views urlpatterns = [ path('logout/',views.mx_logout, name='logout'), ]
注:退出登录功能,没有HTML前端代码
3.4 实现忘记密码、找回密码功能
点击 [忘记密码],进入找回密码,根据注册邮箱,找回密码
3.4.1 根据注册用户邮箱,发送验证邮件
1)views.py:
def forget_pwd(request): """忘记密码,通过邮箱找回密码""" message = {} if request.method == "POST": forget_pwd_form = forms.ForgetPwdForm(request.POST) if forget_pwd_form.is_valid(): email = request.POST.get('email',None) user_objs = UserProfile.objects.filter(email=email) if user_objs: # 判断邮箱是否存在 send_status = email_send.send_register_email(email,'forget') if send_status: # 邮件发送成功 email_send_success = False # 用于前端判断发送邮件的类型 return render(request,'send_email_success.html',locals()) else: message['msg'] = '该邮箱不存在' message['status'] = True return render(request, 'forgetpwd.html', {'message': message, 'forget_pwd_form': forget_pwd_form}) else: # form表单验证不通过 message['msg'] = '邮箱或验证码错误' message['status'] = True return render(request,'forgetpwd.html',{'message':message,'forget_pwd_form' : forget_pwd_form}) else: forget_pwd_form = forms.ForgetPwdForm() return render(request,'forgetpwd.html',{'forget_pwd_form' : forget_pwd_form})
2)forms.py 表单验证:
class ForgetPwdForm(forms.Form): """忘记密码""" email = forms.EmailField(required=True) captcha = CaptchaField(error_messages={'invalid': '验证码错误'})
3)urls.py:
from django.urls import path from web_online import views urlpatterns = [ path('forgetpwd/', views.forget_pwd, name='forgetpwd'), # 忘记密码 ]
4)HTML前端代码:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="renderer" content="webkit"> <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1"> <title>慕学网首页</title> <link rel="stylesheet" type="text/css" href="/static/css/reset.css"> <link rel="stylesheet" type="text/css" href="/static/css/login.css"> </head> <body> <!--提示弹出框--> <div class="successbox dialogbox" id="jsSuccessTips"> <h1>成功提交</h1> <div class="close jsCloseDialog"><img src="/static/images/dig_close.png"/></div> <div class="cont"> <h2>您的需求提交成功!</h2> <p></p> </div> </div> <div class="resetpassbox dialogbox" id="jsSetNewPwd"> <h1>重新设置密码</h1> <div class="close jsCloseDialog"><img src="/static/images/dig_close.png"/></div> <p class="green">请输入新密码</p> <form id="jsSetNewPwdForm"> <div class="box"> <span class="word2">密 码</span> <input type="password" name="password" id="jsResetPwd" placeholder="请输入新密码"/> </div> <div class="box"> <span class="word2">确 认 密 码</span> <input type="password" name="password2" id="jsResetPwd2" placeholder="请再次输入新密码"/> </div> <div class="box"> <span class="word2">验 证 码</span> <input type="text" name="code" id="jsResetCode" placeholder="请输入手机验证码"/> </div> <div class="error btns" id="jsSetNewPwdTips"></div> <div class="button"> <input type="hidden" name="mobile" id="jsInpResetMobil"/> <input id="jsSetNewPwdBtn" type="button" value="提交"/> </div> </form> </div> <div class="bg" id="dialogBg"></div> <header> <div class="c-box fff-box"> <div class="wp header-box"> <p class="fl hd-tips">慕学网,在线学习平台!</p> <ul class="fr hd-bar"> <li>服务电话:<span>33333333</span></li> <li><a href="{% url 'login' %}">[登录]</a></li> <li class="active"><a href="/forgetpwd/">[忘记密码]</a></li> </ul> </div> </div> </header> <section> <div class="c-box bg-box"> <div class="login-box clearfix"> <div class="hd-login clearfix"> <a class="index-logo" href="{% url 'index' %}"></a> <h1>忘记密码</h1> <a class="index-font" href="{% url 'index' %}">回到首页</a> </div> <div class="fl slide"> <div class="imgslide"> <ul class="imgs"> <li><a href=""><img width="483" height="472" src="/static/images/57a801860001c34b12000460.jpg"/></a> </li> <li><a href=""><img width="483" height="472" src="/static/images/57a801860001c34b12000460.jpg"/></a> </li> <li><a href=""><img width="483" height="472" src="/static/images/57a801860001c34b12000460.jpg"/></a> </li> </ul> </div> <div class="unslider-arrow prev"></div> <div class="unslider-arrow next"></div> </div> <div class="fl form-box"> <h2>忘记密码</h2> <form id="jsFindPwdForm" method="post" autocomplete="off"> {% csrf_token %} <div class="form-group marb20 {% if forget_pwd_form.errors.email.0 %} errorput {% endif %}"> <label>帐 号</label> <input type="text" id="account" name="email" {% if message.status %} value="{{ forget_pwd_form.email.value }}" {% endif %} placeholder="请输入邮箱号"/> </div> <div class="form-group captcha1 marb38 {% if forget_pwd_form.errors.captchal.0 %} errorput {% endif %}"> <label>验 证 码</label> {{ forget_pwd_form.captcha }} </div> <div class="error btns" id="jsForgetTips">{{ message.msg }}</div> <input type="hidden" name="sms_type" value="1"> <input class="btn btn-green" id="jsFindPwdBtn" type="submit" value="提交"/> <p class="form-p" style="bottom:40px;">您还可以<a href="{% url 'login' %}"> [直接登录]</a></p> </form> </div> </div> </div> </section> <input id="isLogin" type="hidden" value="False"/> <script src="/static/js/jquery.min.js" type="text/javascript"></script> <script src="/static/js/unslider.js" type="text/javascript"></script> <script src="/static/js/validateDialog.js" type="text/javascript"></script> <script src="/static/js/login.js" type="text/javascript"></script> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {% if email_send_success %} <p>【注册账号激活】邮件已发送,请移步邮箱:{{ user_name }} 中查收!</p> {% else %} <p>【密码重置】邮件已发送,请移步邮箱:{{ email }} 中查收!</p> {% endif %} </body> </html>
5)email_send
【密码重置】发送邮件的相关代码与用户注册账号激活整合在一起,详见用户注册账号激活 --发送邮件
3.4.2 重置密码
1)forms.py:
class ModifyPwdForm(forms.Form): """重置密码""" password1 = forms.CharField(required=True, min_length=6) password2 = forms.CharField(required=True, min_length=6) def clean_password1(self): if self.cleaned_data.get('password1').isdigit() or self.cleaned_data.get('password1').isalpha(): raise ValidationError('密码必须包含数字和字母') else: return self.cleaned_data['password1']
2)views.py:
# 确定当前用户邮箱是否正确,如正确转到重置密码页面,进行密码重置 def pwd_reset(request,ac_code): """用户重置密码链接""" if request.method =="GET": records = EmailVerifyRecord.objects.filter(code=ac_code) if records: email = records[0].email return render(request, "password_reset.html", {"email": email}) else:# 链接不对 return render(request, "active_fail.html")
# 重置密码 def modify_pwd(request): """重置密码""" if request.method == "POST": modify_form = forms.ModifyPwdForm(request.POST) if modify_form.is_valid(): pwd1 = request.POST.get("password1", None) pwd2 = request.POST.get("password2", None) # email数据是从pwd_reset获取到的 email = request.POST.get("email", None) if pwd1 != pwd2: return render(request, "password_reset.html", {"email": email, "msg": "密码不一致!"}) user = UserProfile.objects.get(email=email) user.password = make_password(pwd2) user.save() return render(request, "login.html") else: email = request.POST.get("email", None) return render(request, "password_reset.html", {"email": email, "modify_form": modify_form}) else: return render(request, 'password_reset.html')
3)urls.py配置
from django.urls import path from web_online import views urlpatterns = [ re_path(r'^reset/(\w+)/',views.pwd_reset,name='reset'), # 邮箱重置密码链接 path('modify_pwd/',views.modify_pwd,name='modify_pwd'), # 重置密码 ]
4)HTML前端代码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="renderer" content="webkit"> <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1"> <title>密码修改</title> <link rel="stylesheet" type="text/css" href="/static/css/reset.css"> <link rel="stylesheet" type="text/css" href="/static/css/animate.css"> <link rel="stylesheet" type="text/css" href="/static/css/style.css"> <body> <div class="wp"> <div class="resetpassword" id="resetPwdForm"> <h1>修改密码</h1> <p>已经通过验证,请设置新密码</p> <form id="reset_password_form" action="{% url 'modify_pwd' %}" method="post"> {% csrf_token %} <ul> <li class="{% if modify_form.errors.password1.0 %} errorput {% endif %}"> <span class="">新 密 码 :</span> <input type="password" name="password1" id="pwd" placeholder="6-20位非中文字符"> <i></i> </li> <input type="hidden" name="email" value="{{ email }}"> <li class="{% if modify_form.errors.password2.0 %} errorput {% endif %}"> <span class="">再次输入密码 :</span> <input type="password" name="password2" id="repwd" placeholder="6-20位非中文字符"> <i></i> </li> <div class="error btns" id="jsPasswdResetTips" style="color: red"> {% for key,error in modify_form.errors.items %}{{ key }}:{{ error }}{% endfor %} {{ msg }} </div> <li class="button"> <input type="submit" value="提交"> </li> </ul> </form> </div> <div class="resetpassword" id="reset_password_tips" style="display:none;"> <h1>修改密码成功,请重新登录</h1> <img class="fl" src="/static/images/check2.png"> <p class="successword">已经成功修改密码,请重新登录</p> </div> </div> </body> </html>
未完待续。。