Django+xadmin 打造线上教育平台(二)
一、首页
1)views.py/index函数:
def index(request): """首页""" return render(request,"index.html")
2)html页面:
课程机构列表 - 慕学在线网 { # 轮播图#}
专业权威
课程最新
名师授课
数据真实
3)Mxonline/url.py配置:
from django.urls import path,include,re_pathfrom MxOnline import viewsurlpatterns = [ path('index/', views.index, name = 'index'),]
二、登录
涉及:
- django自带form表单验证
- django自带authenticate验证方法
当用户登录进行authenticate时,会转到我们重新的CustomBackend类进行authenticate验证 ,前提需要将authenticate认证相关配置配置到setting中
1、django自带form表单验证:
LoginForm:
class LoginForm(forms.Form): '''登录验证表单''' username = forms.CharField(required=True) password = forms.CharField(required=True,min_length=5)
2、重写django自带authenticate验证:
1)在setting.py中配置:
AUTHENTICATION_BACKENDS = ( # 登录认证设置 'MxOnline.views.CustomBackend', #CustomBackend所在路径)
2)在MxOnline/views中创建CustomBackend类:
from django.contrib.auth.backends import ModelBackendfrom users.models import UserProfileclass CustomBackend(ModelBackend): """ 用于mx_login下的authenticate, setting中需配置全局路径AUTHENTICATION_BACKENDS,当用户登录验证时,用到mx_login下的authenticate进行验证,会 跳到此处进行验证 """ def authenticate(self, request, username=None, password=None, **kwargs):# 重写authenticate方法 try: # 不希望用户存在两个,get的结果只能有一个,否则报错 user = UserProfile.objects.get(Q(username=username)|Q(email=username)) print(user) # django的后台中密码是加密处理的,拿到客户登录的密码需要加密后才能对比判断,所以不能直接password==password # UserProfile继承的AbstractUser中有check_password()方法,会直接将传入的密码加密后于后台的作比较: print(user.check_password(password)) if user.check_password(password):# 如果为False,则密码不一致,否则密码正确 return user except Exception as e: return None
3)views.py/login:
from django.shortcuts import render,redirect,HttpResponsefrom django.contrib.auth import authenticate ,login ,logoutdef mx_login(request): """登录""" login_form = forms.LoginForm() if request.method == "POST": login_form = forms.LoginForm(request.POST) if login_form.is_valid():# form验证通过 # 获取用户提交的用户名和密码 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: if user.is_active: # 只有注册激活才能登录 login(request, user) # request.session["is_login"] = True # request.session["username"] = user_name # request.session.set_expiry(5) 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',{'login_form': login_form}) return render(request,'login.html')
4)url.py配置:
path('login/', views.mx_login, name = 'login'),
5)html页面:
慕学在线网登录 成功提交
您的需求提交成功!
慕学在线网,在线学习平台!
三、退出登录
1、views.py/logout:
def mx_logout(request): """退出登录""" logout(request) return render(request,'index.html')
2、url.py:
path('logout/', views.mx_logout, name = 'logout'),
无HTML页面:
四、注册
客户注册,涉及验证码方面,需要安装captcha相关
步骤:
- 客户注册账号,验证账号、密码、验证码
- 客户提交注册信息,系统自动给客户回复一封邮件,用于激活账号
- 客户收到邮件并打开激活,激活成功可正常登录
1、views.py/register:
from django.shortcuts import render,redirect,HttpResponsefrom MxOnline import formsfrom users.models import UserProfilefrom django.contrib.auth.hashers import make_passwordfrom utils import email_senddef 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.send_register_email(user_name,'register') # 发送邮件,用户激活账号 return redirect('/login/') else: status_form = True #用于前端注册时判断是否填充客户输入原数据 return render(request,'register.html',locals()) else: register_form = forms.RegisterForm() return render(request,'register.html',{ 'register_form':register_form})
涉及到django自带的邮件功能:
1)setting.py中配置email相关:
# 邮箱配置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 = "name@163.com" #我的163邮箱帐号EMAIL_PORT = 25 #163邮箱SMTP服务端口EMAIL_USE_TLS = True # 163、qq邮箱此值为True,aliyun此值为False,163可以忽略此值EMAIL_SUBJECT_PREFIX = '[yshblog.com]' #邮件标题前缀,默认是'[django]'
2)新建email_send.py:
- 生成随机验证码
- 发送邮件相关
import randomfrom users.models import EmailVerifyRecordfrom django.core.mail import send_mailfrom MxOnline 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
3)url.py配置:
path('register/', views.register, name = 'register'), # 注册
3.1)发送邮件后客户打开邮件激活账号,需要再user_active(用户账号激活)函数:
def user_active(request,ac_code): """用户账号激活""" if request.method == "GET": ac_record = EmailVerifyRecord.objects.filter(code=ac_code,) if ac_record: ac_email = ac_record[0].email ac_user = UserProfile.objects.get(email=ac_email) ac_user.is_active = True ac_user.save() else: return render(request,'active_fail.html') email_count = ac_record[0].email # return render(request,'login.html',{'email_count':email_count}) return render(request,'active_success.html',{ 'email_count':email_count})
3.2)url.py配置:
re_path(r'^active/(\w+)/',views.user_active,name='user_active'),# 邮箱激活账号
4)HTML页面:
{ % load staticfiles %}慕学在线注册 账号登录
成功提交
您的需求提交成功!
慕学在线,在线学习平台!
至此,注册部分便完成了
五、忘记密码
步骤:
- 客户忘记密码,点击‘忘记密码’操作
- 根据客户提交用户信息,系统自动给用户发送邮件用于重置密码
- 客户打开邮件中提供的链接,进行密码重置
- 服务端更新该用户数据
1、忘记密码:
1.1)html页面代码:
慕学网首页 成功提交
您的需求提交成功!
重新设置密码
请输入新密码
慕学网,在线学习平台!
忘记密码
1.2)views.py/forget_pwd:
def forget_pwd(request): """忘记密码""" message ={} if request.method == "POST": forgetpwd_form = forms.ForgetPwdForm(request.POST) if forgetpwd_form.is_valid(): email = request.POST.get('email',None) user_count = UserProfile.objects.filter(email=email) if user_count:# 判断邮箱是否存在 send_status = email_send.send_register_email(email,'forget') # print("send:",send_status) if send_status: return render(request,'send_email_success.html') else: message['msg'] = '该邮箱不存在' message['status'] =True return render(request,'forgetpwd.html',{ 'message':message,'forgetpwd_form':forgetpwd_form}) else: # form表单验证不通过 message['msg'] = '邮箱或验证码错误' message['status'] = True return render(request,'forgetpwd.html',{ 'message':message,'forgetpwd_form':forgetpwd_form}) else: forgetpwd_form = forms.ForgetPwdForm() return render(request,'forgetpwd.html',{ 'forgetpwd_form':forgetpwd_form})
1.3)url.py配置:
path('forgetpwd/',views.forget_pwd,name='forgetpwd'), # 忘记密码
2、发送重置密码邮件后,客户点开邮件中提供的链接,需要再处理:
2.1)views.py/pwd_reset:
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")
2,2)url.py配置:
re_path(r'^reset/(\w+)/',views.pwd_reset,name='reset'), # 邮箱重置密码链接
2.3)无HTML页面,打开重置密码的邮件后,访问邮件内提供的链接,可跳转到重置密码页面:
3、密码重置:
3.1)views.py/modify_pwd:
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 = 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})
3.2)url.py:
path('modify_pwd/',views.modify_pwd,name='modify_pwd'), # 重置密码# 这是验证码的url配置 re_path(r'^captcha', include('captcha.urls')),
3.3)html页面:
密码修改 修改密码
已经通过验证,请设置新密码
至此,用户登录-注册-重置密码方面便完成了
后端完整源码:
views:
from django.shortcuts import render,redirect,HttpResponsefrom django.contrib.auth import authenticate ,login ,logoutfrom django.contrib.auth.backends import ModelBackendfrom MxOnline import formsfrom users.models import UserProfile,EmailVerifyRecordfrom django.db.models import Qfrom django.contrib.auth.hashers import make_passwordfrom utils import email_send# Create your views here.def index(request): """首页""" return render(request,"index.html")class CustomBackend(ModelBackend): """ 用于mx_login下的authenticate, setting中需配置全局路径AUTHENTICATION_BACKENDS,当用户登录验证时,用到mx_login下的authenticate进行验证,会 跳到此处进行验证 """ def authenticate(self, request, username=None, password=None, **kwargs):# 重写authenticate方法 try: # 不希望用户存在两个,get的结果只能有一个,否则报错 user = UserProfile.objects.get(Q(username=username)|Q(email=username)) print(user) # django的后台中密码是加密处理的,拿到客户登录的密码需要加密后才能对比判断,所以不能直接password==password # UserProfile继承的AbstractUser中有check_password()方法,会直接将传入的密码加密后于后台的作比较: print(user.check_password(password)) if user.check_password(password):# 如果为False,则密码不一致,否则密码正确 return user except Exception as e: return Nonedef mx_login(request): """登录""" login_form = forms.LoginForm() if request.method == "POST": login_form = forms.LoginForm(request.POST) if login_form.is_valid():# form验证通过 # 获取用户提交的用户名和密码 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: if user.is_active: # 只有注册激活才能登录 login(request, user) # request.session["is_login"] = True # request.session["username"] = user_name # request.session.set_expiry(5) 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',{'login_form': login_form}) return render(request,'login.html')def mx_logout(request): """退出登录""" logout(request) return render(request,'index.html')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.send_register_email(user_name,'register') # 发送邮件,用户激活账号 return redirect('/login/') else: status_form = True #用于前端注册时判断是否填充客户输入原数据 return render(request,'register.html',locals()) else: register_form = forms.RegisterForm() return render(request,'register.html',{ 'register_form':register_form})def user_active(request,ac_code): """用户账号激活""" if request.method == "GET": ac_record = EmailVerifyRecord.objects.filter(code=ac_code,) if ac_record: ac_email = ac_record[0].email ac_user = UserProfile.objects.get(email=ac_email) ac_user.is_active = True ac_user.save() else: return render(request,'active_fail.html') email_count = ac_record[0].email # return render(request,'login.html',{'email_count':email_count}) return render(request,'active_success.html',{ 'email_count':email_count})def forget_pwd(request): """忘记密码""" message ={} if request.method == "POST": forgetpwd_form = forms.ForgetPwdForm(request.POST) if forgetpwd_form.is_valid(): email = request.POST.get('email',None) user_count = UserProfile.objects.filter(email=email) if user_count:# 判断邮箱是否存在 send_status = email_send.send_register_email(email,'forget') # print("send:",send_status) if send_status: return render(request,'send_email_success.html') else: message['msg'] = '该邮箱不存在' message['status'] =True return render(request,'forgetpwd.html',{ 'message':message,'forgetpwd_form':forgetpwd_form}) else: # form表单验证不通过 message['msg'] = '邮箱或验证码错误' message['status'] = True return render(request,'forgetpwd.html',{ 'message':message,'forgetpwd_form':forgetpwd_form}) else: forgetpwd_form = forms.ForgetPwdForm() return render(request,'forgetpwd.html',{ 'forgetpwd_form':forgetpwd_form})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") # return render(request, "login.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 = 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})
url.py:
"""MxOnline URL ConfigurationThe `urlpatterns` list routes URLs to views. For more information please see: https://docs.djangoproject.com/en/2.0/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.contrib import adminfrom django.urls import path,include,re_pathimport xadminfrom MxOnline import viewsurlpatterns = [ path('xadmin/', xadmin.site.urls), path('index/', views.index, name = 'index'), path('login/', views.mx_login, name = 'login'), path('logout/', views.mx_logout, name = 'logout'), path('register/', views.register, name = 'register'), # re_path('active/(?P.*)/',ActiveUserView.as_view(),name='user_active'), re_path(r'^active/(\w+)/',views.user_active,name='user_active'),# 邮箱激活账号 path('forgetpwd/',views.forget_pwd,name='forgetpwd'), # 忘记密码 re_path(r'^reset/(\w+)/',views.pwd_reset,name='reset'), # 邮箱重置密码链接 path('modify_pwd/',views.modify_pwd,name='modify_pwd'), # 重置密码 # 验证码 re_path(r'^captcha', include('captcha.urls')),]
form.Form自定义表单:
from django import formsfrom captcha.fields import CaptchaFieldclass 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)
email_send.py:
import randomfrom users.models import EmailVerifyRecordfrom django.core.mail import send_mailfrom MxOnline import settingsdef 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