在此之前我们已成功的使用xadmin将后台管理系统搭建起来了,接下来,开始我们后台的功能处理
首先是登录功能
登录功能
(1)把html文件中index.html和login.html拷贝到templates文件夹内
(2)新建static目录用来存放静态文件
记得查看在setting文件,是否配置静态文件路径,
STATIC_URL = ‘/static/’
STATICFILES_DIRS = (os.path.join(BASE_DIR, ‘static’),)
如果没配置请添加上,当然前面我们已经配置了就不用多此一举了
(3)引用静态文件
使用ctrl+f查找出所有“…/”, 然后ctrl+r 全部替换为“/static/”
(4)配置静态文件的url(urls.py)
import xadmin
from django.urls import path
from django.views.generic import TemplateView
urlpatterns = [
path('xadmin/', xadmin.site.urls),
# 首页
path('', TemplateView.as_view(template_name='index.html'),name='index'),
# 登录
path('login/', TemplateView.as_view(template_name='login.html'),name='login'),
]
项目知识:TemplateView
简介:TemplateView 视图类是用来渲染给定的模板文件,其上下文字典包含从 URL 中捕获的参数。
原文:
django基于类的通用视图就是将一些mixin合成为有用的通用视图。基于类的通用视图(以及任何继承了Django提供的基础类的基于类的视图)都能够以下面两种方式被配置:子类化,或者直接通过URLconf来传递参数。
当子类化一个类视图时,可以重写一些属性(比如template_name)或者 一些方法(比如 get_context_data)在子类中来提供一些新的值或者方 法。考虑一下,比如,一个仅仅需要展示一个模板的视图,index.html。Django有一个通用视图来完成这个功能 - TemplateView - 因此可以子类化它,然后重写模板的名称。
需要添加这个新的视图到URLconf配置中。因为类视图本身是一个类,把URL指向 as_view 这个类方法来替代类本身,这是类视图的入口点:
from django.urls import path
from some_app.views import IndexView
urlpatterns = [
path('index/', IndexView.as_view()),
]
作为一个选择,如果仅仅修改类视图中少量简单的属性,可以直接传递新的属性到类本身调用 as_view 方法中:
from django.urls import path
from django.views.generic import TemplateView
urlpatterns = [
path('index/', TemplateView.as_view(template_name="index.html")),
]
(5)更改index.html里面跳转到登录界面的url
原始样子
<!-- <a style="color:white" class="fr registerbtn" href="register.html">注册</a> -->
<!-- <a style="color:white" class="fr loginbtn" href="login.html">登录</a> -->
取消注释,将login.html改为“login/”
<a style="color:white" class="fr registerbtn" href="register.html">注册</a>
<a style="color:white" class="fr loginbtn" href="/login/">登录</a>
现在可以访问index页面,然后点‘’登录”,跳转到登录页面了
以上是前台的基本配置更改,接下来继续
更改login.html
<form action="/login/" method="post" autocomplete="off">
<input type='hidden' name='csrfmiddlewaretoken' value='mymQDzHWl2REXIfPMg2mJaLqDfaS1sD5' />
<div class="form-group marb20 ">
<label>用 户 名</label>
<input name="username" id="account_l" type="text" placeholder="手机号/邮箱" />
</div>
<div class="form-group marb8 ">
<label>密 码</label>
<input name="password" id="password_l" type="password" placeholder="请输入您的密码" />
</div>
<div class="error btns login-form-tips" id="jsLoginTips">{{ msg }}</div>
<div class="auto-box marb38">
<a class="fr" href="forgetpwd.html">忘记密码?</a>
</div>
<input class="btn btn-green" id="jsLoginBtn" type="submit" value="立即登录 > " />
<input type='hidden' name='csrfmiddlewaretoken' value='5I2SlleZJOMUX9QbwYLUIAOshdrdpRcy' />
{% csrf_token %}
</form>
修改index.html
<div class=" header">
<div class="top">
{% if request.user.is_authenticated %}
<div class="personal">
<dl class="user fr">
<dd>bobby<img class="down fr" src="/static/images/top_down.png"/></dd>
<dt><img width="20" height="20" src="/static/media/image/2016/12/default_big_14.png"/></dt>
</dl>
<div class="userdetail">
<dl>
<dt><img width="80" height="80" src="/static/media/image/2016/12/default_big_14.png"/></dt>
<dd>
<h2>django</h2>
<p>bobby</p>
</dd>
</dl>
<div class="btn">
<a class="personcenter fl" href="usercenter-info.html">进入个人中心</a>
<a class="fr" href="/logout/">退出</a>
</div>
</div>
</div>
{% else %}
<div class="wp">
<div class="fl"><p>服务电话:<b>33333333</b></p></div>
<!--登录后跳转-->
<a style="color:white" class="fr registerbtn" href="register.html">注册</a>
<a style="color:white" class="fr loginbtn" href="/login/">登录</a>
</div>
{% endif %}
</div>
接下来是后台代码,首先在apps目录下的users目录创建一个forms文件
# users/forms.py
from django import forms
from captcha.fields import CaptchaField
from .models import UserProfile
class LoginForm(forms.Form):
'''登录验证表单'''
username = forms.CharField(required=True)
password = forms.CharField(required=True,min_length=5)
class RegisterForm(forms.Form):
'''注册验证表单'''
email = forms.EmailField(required=True)
password = forms.CharField(required=True,min_length=5)
# 验证码
captcha = CaptchaField(error_messages={'invalid':'验证码错误'})
class ForgetPwdForm(forms.Form):
'''忘记密码'''
email = forms.EmailField(required=True)
captcha = CaptchaField(error_messages={'invalid': '验证码错误'})
class ModifyPwdForm(forms.Form):
'''重置密码'''
password1 = forms.CharField(required=True, min_length=5)
password2 = forms.CharField(required=True, min_length=5)
class UploadImageForm(forms.ModelForm):
'''用户更改图像'''
class Meta:
model = UserProfile
fields = ['image']
class UserInfoForm(forms.ModelForm):
'''个人中心信息修改'''
class Meta:
model = UserProfile
fields = ['nick_name','gender','birthday','adress','mobile']
users目录下的views文件
# users/views.py
from django.shortcuts import render
from django.contrib.auth import authenticate,login
from django.contrib.auth.backends import ModelBackend
from .models import UserProfile
from django.db.models import Q
from django.views.generic.base import View
from .forms import LoginForm
#邮箱和用户名都可以登录
# 基础ModelBackend类,因为它有authenticate方法
class CustomBackend(ModelBackend):
def authenticate(self, request, username=None, password=None, **kwargs):
try:
# 不希望用户存在两个,get只能有一个。两个是get失败的一种原因 Q为使用并集查询
user = UserProfile.objects.get(Q(username=username)|Q(email=username))
# django的后台中密码加密:所以不能password==password
# UserProfile继承的AbstractUser中有def check_password(self, raw_password):
if user.check_password(password):
return user
except Exception as e:
return None
class LoginView(View):
def get(self,request):
return render(request, 'login.html')
def post(self,request):
# 实例化
login_form = LoginForm(request.POST)
if login_form.is_valid():
# 获取用户提交的用户名和密码
user_name = request.POST.get('username', None)
pass_word = request.POST.get('password', None)
# 成功返回user对象,失败None
user = authenticate(username=user_name, password=pass_word)
# 如果不是null说明验证成功
if user is not None:
# 登录
login(request, user)
return render(request, 'index.html')
# 只有当用户名或密码不存在时,才返回错误信息到前端
else:
return render(request, 'login.html', {'msg': '用户名或密码错误','login_form':login_form})
# form.is_valid()已经判断不合法了,所以这里不需要再返回错误信息到前端了
else:
return render(request,'login.html',{'login_form':login_form})
完善login.html的错误提示信息
<div class="form-group marb20 {% if login_form.errors.username %}errorput{% endif %}">
<label>用 户 名</label>
<input name="username" id="account_l" type="text" placeholder="手机号/邮箱" />
</div>
<div class="form-group marb8 {% if login_form.errors.username %}errorput{% endif %}">
<label>密 码</label>
<input name="password" id="password_l" type="password" placeholder="请输入您的密码" />
</div>
<div class="error btns login-form-tips" id="jsLoginTips">
{% for key,error in login_form.errors.items %}
{{ error }}
{% endfor %}
{{ msg }}
</div>
login.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="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="login.html">[登录]</a></li>
<li><a href="register.html">[注册]</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.html"></a>
<h1>用户登录</h1>
<a class="index-font" href="index.html">回到首页</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">
<input type='hidden' name='csrfmiddlewaretoken' value='mymQDzHWl2REXIfPMg2mJaLqDfaS1sD5' />
<div class="form-group marb20 {% if login_form.errors.username %}errorput{% endif %}">
<label>用 户 名</label>
<input name="username" id="account_l" type="text" placeholder="手机号/邮箱" />
</div>
<div class="form-group marb8 {% if login_form.errors.username %}errorput{% endif %}">
<label>密 码</label>
<input name="password" id="password_l" type="password" placeholder="请输入您的密码" />
</div>
<div class="error btns login-form-tips" id="jsLoginTips">
{% for key,error in login_form.errors.items %}
{{ error }}
{% endfor %}
{{ msg }}
</div>
<div class="auto-box marb38">
<a class="fr" href="forgetpwd.html">忘记密码?</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="register.html">[立即注册]</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>
urls文件,更改登录路由
from apps.users.views import LoginView
path('login/', LoginView.as_view(), name='login'),
settings文件,添加如下代码:
AUTHENTICATION_BACKENDS = (
'apps.users.views.CustomBackend',
)
显示效果,当不输入用户名,密码小与五位数的时候的提示信息如下:
登录时用的用户和密码是,之前咱们创建的超级管理员
在此,登录功能暂时告一段落,接下来是注册功能
注册功能
主要实现功能
- 用户输入邮箱、密码和验证码,点注册按钮
- 如果输入的不正确,提示错误信息
- 如果正确,发送激活邮件,用户通过邮件激活后才能登陆
- 即使注册成功,没有激活的用户也不能登陆
主要技术
- 验证码库:django-simple-captcha
- qq邮箱实现发送激活邮件
首先还是先把我们的register.html C到我们自己的项目下,并修改静态文件地址
修改index.html,点 “注册 ”应该跳到用户注册页面
<a style="color:white" class="fr registerbtn" href="/register/">注册</a>
<a style="color:white" class="fr loginbtn" href="/login/">登录</a>
测试一下从index界面点注册能不能跳到register界面
验证码库:django-simple-captcha
settings文件配置,在INSTALLED_APPS 里添加如下代码
INSTALLED_APPS = [
'captcha',
]
urls文件配置,添加如下代码
urlpatterns = [
path('captcha/',include('captcha.urls')),
]
数据库迁移
makemigrations
migrate
可以看到数据库多了一张表
在注册页面显示验证码, 定义我们的register form:
# users/forms.py
from captcha.fields import CaptchaField
class RegisterForm(forms.Form):
'''注册验证表单'''
email = forms.EmailField(required=True)
password = forms.CharField(required=True,min_length=5)
# 验证码,字段里面可以自定义错误提示信息
captcha = CaptchaField()
修改register页面,通过{{ register_form.captcha }}获取验证码
<div class="form-group marb8 captcha1 ">
<label>验 证 码</label>
{{ register_form.captcha }}
</div>
完善注册的后台逻辑,views文件
# users/views.py
class RegisterView(View):
'''用户注册'''
def get(self,request):
register_form = RegisterForm()
return render(request,'register.html',{'register_form':register_form})
def post(self,request):
register_form = 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', {'register_form':register_form,'msg': '用户已存在'})
pass_word = request.POST.get('password', None)
# 实例化一个user_profile对象
user_profile = UserProfile()
user_profile.username = user_name
user_profile.email = user_name
user_profile.is_active = False
# 对保存到数据库的密码加密
user_profile.password = make_password(pass_word)
user_profile.save()
send_register_eamil(user_name,'register')
return render(request,'login.html')
else:
return render(request,'register.html',{'register_form':register_form})
说明:
- 如果是get请求,直接返回注册页面给用户
- 如果是post请求,先生成一个表单实例,并获取用户提交的所有信息(request.POST)
- is_valid()方法,验证用户的提交信息是不是合法
- 如果合法,获取用户提交的email和password
- 实例化一个user_profile对象,把用户添加到数据库
- 默认添加的用户是激活状态(is_active=1表示True),在这里我们修改默认的状态(改为is_active = False),只有用户去邮箱激活之后才改为True
- 对密码加密,然后保存,发送邮箱,username是用户注册的邮箱,‘register’表明是注册
注册成功跳转到登录界面
发送激活邮件
在Python中已经内置了一个smtp邮件发送模块,Django在此基础上进行了简单地封装,让我们在Django环境中可以更方便更灵活的发送邮件。
所有的功能都在django.core.mail中。
首先settings里面设置
# settings.py
EMAIL_HOST = "smtp.qq.com" # SMTP服务器主机
EMAIL_PORT = 25 # 端口
EMAIL_HOST_USER = "1184405959@qq.com" # 邮箱地址
EMAIL_HOST_PASSWORD = "dwjybikexxxxxxxx" # 密码
EMAIL_USE_TLS= True
EMAIL_FROM = "1184405959@qq.com" # 邮箱地址
要想用qq邮箱作为服务器发送邮件,必须先开启SMTP,方法如下:
1)登录邮箱,找到“设置”–>>“用户”
2)往下拉找到SMTP服务,点开启,然后点“生成授权码”
3)可以看到授权码,“EMAIL_HOST_PASSWORD”里面填写的就是下面生成的授权码,而不是你的邮箱密码
在apps目录新建一个utils目录,然后新建一个email_send.py文件
# apps/utils/email_send.py
from random import Random
from django.core.mail import send_mail
from users.models import EmailVerifyRecord
from MxOnline.settings import EMAIL_FROM
# 生成随机字符串
def random_str(random_length=8):
str = ''
# 生成字符串的可选字符串
chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789'
length = len(chars) - 1
random = Random()
for i in range(random_length):
str += chars[random.randint(0, length)]
return str
# 发送注册邮件
def send_register_eamil(email, send_type="register"):
# 发送之前先保存到数据库,到时候查询链接是否存在
# 实例化一个EmailVerifyRecord对象
email_record = EmailVerifyRecord()
# 生成随机的code放入链接
code = random_str(16)
email_record.code = code
email_record.email = email
email_record.send_type = send_type
email_record.save()
# 定义邮件内容:
email_title = ""
email_body = ""
if send_type == "register":
email_title = "NBA注册激活链接"
email_body = "请点击下面的链接激活你的账号: http://127.0.0.1:8000/active/{0}".format(code)
# 使用Django内置函数完成邮件发送。四个参数:主题,邮件内容,发件人邮箱地址,收件人(是一个字符串列表)
send_status = send_mail(email_title, email_body, EMAIL_FROM, [email])
# 如果发送成功
if send_status:
pass
切记,代码里的链接地址处的端口号,是你自己项目的端口号
项目知识:发送电子邮件
官方文档:
def send_mail(subject, message, from_email, recipient_list,
fail_silently=False, auth_user=None, auth_password=None,
connection=None, html_message=None):
前面四个参数必须要,后面的参数可以为空
发送电子邮件的最简单方法是使用 django.core.mail.send_mail()的subject,message,from_email和recipient_list参数是必需的。
- subject:一个字符串。
- message:一个字符串。
- from_email:一个字符串。
- recipient_list:字符串列表,每个字符串都是电子邮件地址。每个成员都recipient_list将在电子邮件的“收件人:”字段中看到其他收件人。
- fail_silently:一个布尔值。如果是的话False,send_mail会提出一个smtplib.SMTPException。有关smtplib可能的例外列表,请参阅文档,所有这些例外都是。的子类 SMTPException。
- auth_user:用于向SMTP服务器进行身份验证的可选用户名。如果没有提供,Django将使用该EMAIL_HOST_USER设置的值 。
- auth_password:用于验证SMTP服务器的可选密码。如果没有提供,Django将使用该EMAIL_HOST_PASSWORD设置的值 。
- connection:用于发送邮件的可选电子邮件后端。如果未指定,将使用默认后端的实例。有关 更多详细信息,请参阅电子邮件后端的文档。
- html_message:如果html_message被提供,所得到的电子邮件将是一个 多部分/替代电子邮件message作为 文本/无格式内容类型和html_message作为 text / html的内容类型。
- 返回值将是成功传递消息的数量(可以是0或1因为它只能发送一条消息)。
在templates目录下创建 active_fail.html,代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<p style="color: red;">链接失效</p>
</body>
</html>
激活邮箱的邮件如下:
修改login视图
添加一个判断,用户注册的后,等激活才能登陆
# users/views.py
from django.shortcuts import render
from django.contrib.auth import authenticate,login
from django.contrib.auth.backends import ModelBackend
from .models import UserProfile,EmailVerifyRecord
from django.db.models import Q
from django.views.generic.base import View
from .forms import LoginForm,RegisterForm
from django.contrib.auth.hashers import make_password
from utils.email_send import send_register_eamil
#邮箱和用户名都可以登录
# 基础ModelBackend类,因为它有authenticate方法
class CustomBackend(ModelBackend):
def authenticate(self, request, username=None, password=None, **kwargs):
try:
# 不希望用户存在两个,get只能有一个。两个是get失败的一种原因 Q为使用并集查询
user = UserProfile.objects.get(Q(username=username)|Q(email=username))
# django的后台中密码加密:所以不能password==password
# UserProfile继承的AbstractUser中有def check_password(self, raw_password):
if user.check_password(password):
return user
except Exception as e:
return None
class LoginView(View):
'''用户登录'''
def get(self,request):
return render(request, 'login.html')
def post(self,request):
# 实例化
login_form = LoginForm(request.POST)
if login_form.is_valid():
# 获取用户提交的用户名和密码
user_name = request.POST.get('username', None)
pass_word = request.POST.get('password', None)
# 成功返回user对象,失败None
user = authenticate(username=user_name, password=pass_word)
# 如果不是null说明验证成功
if user is not None:
if user.is_active:
# 只有注册激活才能登录
login(request, user)
return render(request, 'index.html')
else:
return render(request, 'login.html', {'msg': '用户名或密码错误', 'login_form': login_form})
# 只有当用户名或密码不存在时,才返回错误信息到前端
else:
return render(request, 'login.html', {'msg': '用户名或密码错误','login_form':login_form})
# form.is_valid()已经判断不合法了,所以这里不需要再返回错误信息到前端了
else:
return render(request,'login.html',{'login_form':login_form})
# 激活用户的view
class ActiveUserView(View):
def get(self, request, active_code):
# 查询邮箱验证记录是否存在
all_record = EmailVerifyRecord.objects.filter(code = active_code)
if all_record:
for record in all_record:
# 获取到对应的邮箱
email = record.email
# 查找到邮箱对应的user
user = UserProfile.objects.get(email=email)
user.is_active = True
user.save()
# 激活成功跳转到登录页面
return render(request, "login.html", )
# 自己瞎输的验证码
else:
return render(request, "register.html", {"msg": "您的激活链接无效"})
class RegisterView(View):
'''用户注册'''
def get(self,request):
register_form = RegisterForm()
return render(request,'register.html',{'register_form':register_form})
def post(self,request):
register_form = RegisterForm(request.POST)
if register_form.is_valid():
user_name = request.POST.get('email', None)
pass_word = request.POST.get('password', None)
# 实例化一个user_profile对象
user_profile = UserProfile()
user_profile.username = user_name
user_profile.email = user_name
user_profile.is_active = False
# 对保存到数据库的密码加密
user_profile.password = make_password(pass_word)
user_profile.save()
send_register_eamil(user_name,'register')
return render(request,'login.html')
else:
return render(request,'register.html',{'register_form':register_form})
修改register.html
<form id="email_register_form" method="post" action="{% url 'register' %}" autocomplete="off">
<input type='hidden' name='csrfmiddlewaretoken' value='gTZljXgnpvxn0fKZ1XkWrM1PrCGSjiCZ' />
<div class="form-group marb20 {% if login_form.errors.email %}errorput{% endif %}">
<label>邮 箱</label>
<input type="text" id="id_email" name="email" value="{{ register_form.email.value }}" placeholder="请输入您的邮箱地址" />
</div>
<div class="form-group marb8 {% if login_form.errors.password %}errorput{% endif %}">
<label>密 码</label>
<input type="password" id="id_password" name="password" value="{{ register_form.password.value }}" placeholder="请输入6-20位非中文字符密码" />
</div>
<div class="form-group marb8 captcha1 {% if login_form.errors.captchal %}errorput{% endif %}">
<label>验 证 码</label>
{{ register_form.captcha }}
</div>
<div class="error btns" id="jsEmailTips">
{% for key,error in register_form.errors.items %}
{{ error }}
{% endfor %}
{{ msg }}
</div>
<div class="auto-box marb8">
</div>
<input class="btn btn-green" id="jsEmailRegBtn" type="submit" value="注册并登录" />
<input type='hidden' name='csrfmiddlewaretoken' value='5I2SlleZJOMUX9QbwYLUIAOshdrdpRcy' />
{% csrf_token %}
</form>
添加邮件激活的url
# MxOnline/urls.py
import xadmin
from django.urls import path,include,re_path
from django.views.generic import TemplateView
from users.views import LoginView,RegisterView,ActiveUserView
urlpatterns = [
path('xadmin/', xadmin.site.urls),
path('', TemplateView.as_view(template_name='index.html'),name='index'),
path('login/',LoginView.as_view(),name = 'login'),
path('register/',RegisterView.as_view(),name = 'register'),
path('captcha/',include('captcha.urls')),
re_path('active/(?P<active_code>.*)/',ActiveUserView.as_view(),name='user_active'),
]
至此,注册功能暂时告一段落,接下是忘记密码
忘记密码
主要需要实现的功能:
- 用户点“忘记密码”,跳到找回密码页面
- 在forgetpwd页面,输入邮箱和验证码成功后,发送邮件提醒
- 通过点击邮件链接,可以重置密码
- 两次密码输的正确无误后,密码更新成功,跳到登录界面
首先是路由设计(urls.py)
from users.views import ForgetPwdView
urlpatterns = [
path('forget/',ForgetPwdView.as_view(),name='forget_pwd'),
]
模板修改,修改login.html中的url
<a class="fr" href="{% url 'forget_pwd' %}">忘记密码?</a>
把forgetpwd.html拷贝到templates文件下,并修改静态文件路径
显示验证码
<div class="form-group captcha1 marb38">
<label>验 证 码</label>
{{ forget_pwd.captcha }}
</div>
初步视图函数
首先也需要个表单,forms.py
class ForgetPwdForm(forms.Form):
'''忘记密码'''
email = forms.EmailField(required=True)
captcha = CaptchaField(error_messages={'invalid': '验证码错误'})
视图函数
class ForgetPwdView(View):
'''找回密码'''
def get(self,request):
forget_form = ForgetPwdForm()
return render(request,'forgetpwd.html',{'forget_form':forget_form})
get方式,直接返回忘记密码的表单
添加发送找回密码邮件,修改utils/email_send.py
发送类型为“forget”
if send_type == "forget":
email_title = "NBA找回密码链接"
email_body = "请点击下面的链接找回你的密码: http://127.0.0.1:8000/reset/{0}".format(code)
# 使用Django内置函数完成邮件发送。四个参数:主题,邮件内容,从哪里发,接受者list
send_status = send_mail(email_title, email_body, EMAIL_FROM, [email])
# 如果发送成功
if send_status:
pass
完善找回密码的views
class ForgetPwdView(View):
'''找回密码'''
def get(self,request):
forget_form = ForgetPwdForm()
return render(request,'forgetpwd.html',{'forget_form':forget_form})
def post(self,request):
forget_form = ForgetPwdForm(request.POST)
if forget_form.is_valid():
email = request.POST.get('email',None)
send_register_eamil(email,'forget')
return render(request, 'send_success.html')
else:
return render(request,'forgetpwd.html',{'forget_form':forget_form})
用户提交邮箱后,提醒成功发送邮件,新建templates/send_success.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<p>邮件已发送,请注意查收</p>
</body>
</html>
修改forgetpwd.html
<form id="jsFindPwdForm" method="post" action="{% url 'forget_pwd' %}" autocomplete="off">
<input type='hidden' name='csrfmiddlewaretoken' value='mymQDzHWl2REXIfPMg2mJaLqDfaS1sD5'/>
<div class="form-group marb20 {% if forget_from.errors.email %}errorput{% endif %}">
<label>帐 号</label>
<input type="text" id="account" name="email" value="{{ forget_from.email.value }}"
placeholder="邮箱"/>
</div>
<div class="form-group captcha1 marb38 {% if forget_from.errors.captchal %}errorput{% endif %}">
<label>验 证 码</label>
{{ forget_form.captcha }}
</div>
<div class="error btns" id="jsForgetTips">
{% for key,error in forget_from.errors.items %}
{{ error }}
{% endfor %}
{{ 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="login.html"> [直接登录]</a></p>
<input type='hidden' name='csrfmiddlewaretoken' value='5I2SlleZJOMUX9QbwYLUIAOshdrdpRcy'/>
{% csrf_token %}
</form>
测试一下,输入邮箱和验证码看能不能收到邮件
重置密码
(1)重置密码激活邮箱的url
re_path('reset/(?P<active_code>.*)/', ResetView.as_view(), name='reset_pwd'),
(2)写重置密码(get方式)后台逻辑
class ResetView(View):
def get(self, request, active_code):
all_records = EmailVerifyRecord.objects.filter(code=active_code)
if all_records:
for record in all_records:
email = record.email
return render(request, "password_reset.html", {"email":email})
else:
return render(request, "active_fail.html")
return render(request, "login.html")
(3)创建修改密码的form表单
class ModifyPwdForm(forms.Form):
'''重置密码'''
password1 = forms.CharField(required=True, min_length=5)
password2 = forms.CharField(required=True, min_length=5)
(4)修改密码的url
上面那个是激活邮箱的url,有active_code参数,只能写get方式的逻辑。
这里必须单独新建一个修改密码的url,因为如果以post方式提交的话,post提交的地方跟get方式(url中需要active_code参数)的地址不一样,action="{% url ‘modify_pwd’ %}
path('modify_pwd/', ModifyPwdView.as_view(), name='modify_pwd'),
(5)修改密码的后台逻辑
class ModifyPwdView(View):
def post(self, request):
modify_form = ModifyPwdForm(request.POST)
if modify_form.is_valid():
pwd1 = request.POST.get("password1", "")
pwd2 = request.POST.get("password2", "")
email = request.POST.get("email", "")
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", "")
return render(request, "password_reset.html", {"email":email, "modify_form":modify_form })
(6)修改password_reset.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">
<ul>
<li class="{% if modify_form.errors.password1 %}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 %}errorput{% endif %}">
<span class="">确定密码:</span>
<input type="password" name="password2" id="repwd" placeholder="6-20位非中文字符">
<i></i>
</li>
<div class="error btns" id="jsPasswdResetTips">
{% for key,error in modify_form.errors.items %}{{ key }}:{{ error }}{% endfor %}{{ msg }}</div>
<li class="button">
<input type="submit" value="提交">
</li>
</ul>
{% csrf_token %}
</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>
运行项目,查看效果
修改密码后,跳转到login页面