用户注册功能实现
首页和登录页面的配置
导入index.html,建立static目录,在settings.py中配置静态文件static所在目录,配置urls.py,处理index页面
同理配置login.html页面
setting中配置静态文件目录
# 说明静态文件放在哪个目录
STATIC_URL = '/static/'
STATICFILES_DIRS = (
os.path.join(BASE_DIR, "static"),
)
在static中加入静态文件:css image js 等
配置点击登录之后的跳转页面url
# 基于类方法实现登录,这里是调用它的方法
url('^login/$', LoginView.as_view(), name="login"),
用户登录
基于函数来完成登录
配置users/views.py中login函数,urls.py中login 的URL指向该函数
login.html中添加,这是Django的一种安全机制,防止一些攻击对服务器造成的压力
{% csrf_token %}
user/view.py
Request为post时,取到前端页面的用户名和密码,用Django自带的authenticate方法进行验证,在user/views.py中import该方法,如果用户名、密码正确,直接调用login方法,进行登录,登陆成功后跳转到Index.html页面中,在index页面中根据登录的user的is_authenticated方法进行判断,从而显示不同页面。
自定义authentic方法,实现邮箱、用户名均可登录
# 当我们配置url被这个view处理时,自动传入request对象.
def user_login(request):
# 前端向后端发送的请求方式: get 或post
# 登录提交表单为post
if request.method == "POST":
# 取不到时为空,username,password为前端页面name值
user_name = request.POST.get("username", "")
pass_word = request.POST.get("password", "")
# 成功返回user对象,失败返回null
user = authenticate(username=user_name, password=pass_word)
# 如果不是null说明验证成功
if user is not None:
# login_in 两参数:request, user
# 实际是对request写了一部分东西进去,然后在render的时候:
# request是要render回去的。这些信息也就随着返回浏览器。完成登录
login(request, user)
# 跳转到首页 user request会被带回到首页
return render(request, "index.html")
# 没有成功说明里面的值是None,并再次跳转回主页面
else:
return render(request, "login.html", {"msg": "用户名或密码错误! "})
# 获取登录页面为get
elif request.method == "GET":
# render就是渲染html返回用户
# render三变量: request 模板名称 一个字典写明传给前端的值
return render(request, "login.html", {})
如果密码验证失败,将错误信息{"msg": "用户名或密码错误! "}返回到前台页面
{{ msg }}
配置可以通过邮箱登录
user/views.py中定义一个类CustomBackend继承ModelBackend类
from .models import UserProfile
# 并集运算
from django.db.models import Q
from django.contrib.auth.backends import ModelBackend
# 实现用户名邮箱均可登录
# 继承ModelBackend类,因为它有方法authenticate,可点进源码查看
class CustomBackend(ModelBackend):
def authenticate(self, 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
在settings.py文件中重载变量BACKENDS指定views中的类
# 设置邮箱和用户名均可登录
AUTHENTICATION_BACKENDS = (
'users.views.CustomBackend',
)
也可以使用基于类的方式实现登录,views中定义LoginView类,继承View类,并在urls.py中进行更改,这种方法更为简便
基于类的方式登录
user/view.py
# 基于类实现需要继承的view
from django.views.generic.base import View
class LoginView(View):
# 直接调用get方法免去判断
def get(self, request):
# render就是渲染html返回用户
# render三变量: request 模板名称 一个字典写明传给前端的值
redirect_url = request.GET.get('next', '')
return render(request, "login.html", {
"redirect_url": redirect_url
})
def post(self, request):
# 类实例化需要一个字典参数dict:request.POST就是一个QueryDict所以直接传入
# POST中的usernamepassword,会对应到form中
login_form = LoginForm(request.POST)
# is_valid判断我们字段是否有错执行我们原有逻辑,验证失败跳回login页面
if login_form.is_valid():
# 取不到时为空,username,password为前端页面name值
user_name = request.POST.get("username", "")
pass_word = request.POST.get("password", "")
# 成功返回user对象,失败返回null
user = authenticate(username=user_name, password=pass_word)
# 如果不是null说明验证成功
if user is not None:
# 只有当用户**时才给登录
if user.is_active:
# login_in 两参数:request, user
# 实际是对request写了一部分东西进去,然后在render的时候:
# request是要render回去的。这些信息也就随着返回浏览器。完成登录
login(request, user)
# 跳转到首页 user request会被带回到首页
# 跳转到首页 user request会被带回到首页
# 增加重定向回原网页。
redirect_url = request.POST.get('next', '')
if redirect_url:
return HttpResponseRedirect(redirect_url)
return HttpResponseRedirect(reverse("index"))
# 即用户未**跳转登录,提示未**
else:
return render(
request, "login.html", {
"msg": "用户名未**! 请前往邮箱进行**"})
# 仅当用户真的密码出错时
else:
return render(request, "login.html", {"msg": "用户名或密码错误!"})
# 验证不成功跳回登录页面
# 没有成功说明里面的值是None,并再次跳转回主页面
else:
return render(
request, "login.html", {
"login_form": login_form})
相应改变url.py
from users.views import LoginView
# 基于类方法实现登录,这里是调用它的方法
url('^login/$', LoginView.as_view(), name="login"),
用form实现登录
新建user/forms.py
保证form中的用户输入信息名称与前端html文件一致,这样才可以进行form的验证
# 引入Django表单
from django import forms
# 登录表单验证
class LoginForm(forms.Form):
# 用户名密码不能为空
username = forms.CharField(required=True)
# 密码不能小于5位
password = forms.CharField(required=True, min_length=5)
将form的逻辑加入到view.py文件中,处理数据login_form、login_form.is_valid()用来验证
将form验证后产生的信息,显示在前端页面
{% if login_form.username.errors %} errorput{% endif %}
{% if login_form.password.errors %} errorput{% endif %}
session和cookie自动登录机制
http的请求本身很单一,如下:
因而我们引出了cookie:实质上就是浏览器的一种本地存储机制
Cookie通过在客户端记录信息确定用户身份,客户端请求服务器,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个Cookie。客户端浏览器会把Cookie保存起来。当浏览器再请求该网站时,浏览器把请求的网址连同该Cookie一同提交给服务器。服务器检查该Cookie,以此来辨认用户状态。服务器还可以根据需要修改Cookie的内容。
但是cookie也就有了一些安全问题,比如把用户信息存在本地,容易被窃取。因而服务器在返回cookie时用到了一种session(存储于服务器端的)的机制,生成一段随机字符串发送给用户,用户存储在cookie当中,在下一次请求时,用户把session值带回到服务器,服务器通过session id 在数据库中查询,看是哪个用户,这样就可以对该用户进行标记。如此,Django利用cookie和session机制实现了自动登录
解决cookie放在本地不安全的问题(session),用户在第一次请求后,浏览器回复的id既可以是用户的user id,也可以一段任意的字符串,我们把它叫做session id根据用户名和密码,服务器会采用自己的规则生成session id。这个session id保存在本地cookie。浏览器请求服务器会携带。Django的默认表中的session表就记录了用户登录时,后端我们Django为用户生成的sessionid。通过session id 用户访问任何一个页面都会携带,服务器就会认识。Django的APP之一session会拦截我们每次的request请求,在request中找到session id,然后去数据表中进行查询,然后通过session key 去找到session data。此时直接为我们取出了user。
用户注册
注册页面templates/register.html
urls.py文件中定义
from users.views import RegisterView
# 注册url
url("^register/", RegisterView.as_view(), name="register"),
在html文件中使用url指定跳转
{% url 'register' %}
修改静态文件的路径
GitHub上下载验证码库,实现验证码 captcha,添加到settings.py的INSTALLED_APPS中,配置URL
from django.conf.urls import url, include
# 验证码 url配置
url(r'^captcha/', include('captcha.urls')),
Makemigrations & migrate生成对应表。
user/forms.py中定义form
# 引入验证码field
from captcha.fields import CaptchaField
# 验证码form & 注册表单form
class RegisterForm(forms.Form):
# 此处email与前端name需保持一致。
email = forms.EmailField(required=True)
# 密码不能小于5位
password = forms.CharField(required=True, min_length=5)
# 应用验证码 自定义错误输出key必须与异常一样
captcha = CaptchaField(error_messages={"invalid": u"验证码错误"})
user/views.py
# 进行密码加密
from django.contrib.auth.hashers import make_password
from .forms import LoginForm, RegisterForm
# 发送邮件
from utils.email_send import send_register_eamil
# 注册功能的view
class RegisterView(View):
# get方法直接返回页面
def get(self, request):
# 添加验证码
register_form = RegisterForm()
return render(
request, "register.html", {
'register_form': register_form})
def post(self, request):
# 实例化form
register_form = RegisterForm(request.POST)
if register_form.is_valid():
# 这里注册时前端的name为email
user_name = request.POST.get("email", "")
if UserProfile.objects.filter(email=user_name):
return render(
request, "register.html", {
"register_form": register_form, "msg": "用户已经存在"})
pass_word = request.POST.get("password", "")
# 实例化一个user_profile对象,将前台值存入
user_profile = UserProfile()
user_profile.username = user_name
user_profile.email = user_name
# 默认**状态为false
user_profile.is_active = False
# 加密password进行保存
user_profile.password = make_password(pass_word)
user_profile.save()
# 写入欢迎注册消息
user_message = UserMessage()
user_message.user = user_profile.id
user_message.message = "欢迎注册VictoriaJiang云教育平台!!"
user_message.save()
# 发送注册**邮件
send_register_eamil(user_name, "register")
# 跳转到登录页面
return render(request, "login.html", )
# 注册邮箱form验证失败
else:
return render(
request, "register.html", {
"register_form": register_form})
在html文件中,传入验证码
{{ register_form.captcha }}
注册邮件发送
apps/utils/send_email.py中定义发送邮件的函数(其他地方也会用到,放在这里可以引用)
邮箱验证码功能如何完成:在用户的链接中添加随机字符串,字符串由后台生成,即EmailVerifyRecord中定义的code,用户在点击这个URL链接时,我们把这个code取出来,再回来查询数据库,查看这个随机字符串是否存在,如果存在,则**,否则报错。
from users.models import EmailVerifyRecord
# 导入Django自带的邮件模块
from django.core.mail import send_mail,EmailMessage
# 导入setting中发送邮件的配置
from Mxoline2.settings import EMAIL_FROM
# 发送html格式的邮件:
from django.template import loader
# 生成随机字符串
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放入链接
if send_type == "update_email":
code = random_str(4)
else:
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 = "VictoriaJiang云教育平台 注册**链接"
email_body = loader.render_to_string(
"email_register.html", # 需要渲染的html模板
{
"active_code":code # 参数
}
)
msg = EmailMessage(email_title, email_body, EMAIL_FROM, [email])
msg.content_subtype = "html"
send_status = msg.send()
# 使用Django内置函数完成邮件发送。四个参数:主题,邮件内容,从哪里发,接受者list
# send_status = send_mail(email_title, email_body, EMAIL_FROM, [email])
# 如果发送成功
if send_status:
pass
elif send_type == "forget":
email_title = "VictoriaJiang云教育平台 找回密码链接"
email_body = loader.render_to_string(
"email_forget.html", # 需要渲染的html模板
{
"active_code": code # 参数
}
)
msg = EmailMessage(email_title, email_body, EMAIL_FROM, [email])
msg.content_subtype = "html"
send_status = msg.send()
elif send_type == "update_email":
email_title = "VictoriaJiang云教育平台 修改邮箱验证码"
email_body = loader.render_to_string(
"email_update_email.html", # 需要渲染的html模板
{
"active_code": code # 参数
}
)
msg = EmailMessage(email_title, email_body, EMAIL_FROM, [email])
msg.content_subtype = "html"
send_status = msg.send()
setting中配置发送邮件参数
# 发送邮件的setting设置
EMAIL_HOST = "smtp.163.com"
EMAIL_PORT = 25
EMAIL_HOST_USER = "[email protected]"
EMAIL_HOST_PASSWORD = "xxxxxxxxxx"
EMAIL_USE_TLS= True
EMAIL_FROM = "[email protected]"
邮箱**
urls.py
from users.views import LoginView, RegisterView, ActiveUserView
# **用户
url(r'^active/(?P<active_code>.*)/$',ActiveUserView.as_view(), name= "user_active"),
user/view.py
# **用户的view
class ActiveUserView(View):
def get(self, request, active_code):
# 查询箱验证记录是否存在
all_record = EmailVerifyRecord.objects.filter(code=active_code)
# 如果不为空也就是有用户
active_form = ActiveForm(request.GET)
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": "您的**链接无效", "active_form": active_form})
找回密码
忘记密码
定义url
# 忘记密码
url(r'^forget/$', ForgetPwdView.as_view(), name="forget_pwd"),
新建forgetpsw.html
user/forms.py 定义form
# 忘记密码实现
class ForgetForm(forms.Form):
# 此处email与前端name需保持一致。
email = forms.EmailField(required=True)
# 应用验证码 自定义错误输出key必须与异常一样
captcha = CaptchaField(error_messages={"invalid": u"验证码错误"})
user/view.py
class ForgetPwdView(View):
# get方法直接返回页面
def get(self, request):
forget_from = ForgetForm()
return render(request, "forgetpwd.html", {"forget_from": forget_from})
# post方法实现
def post(self, request):
forget_form = ForgetForm(request.POST)
# form验证合法情况下取出email
if forget_form.is_valid():
email = request.POST.get("email", "")
# 发送找回密码邮件
send_register_eamil(email, "forget")
# 发送完毕返回登录页面并显示发送邮件成功。
return render(request, "login.html", {"msg": "重置密码邮件已发送,请注意查收"})
# 如果表单验证失败也就是他验证码输错等。
else:
return render(
request, "forgetpwd.html", {
"forget_from": forget_form})
在send_email.py文件中定义forget类型的发送
重置密码
urls.py
# 重置密码urlc :用来接收来自邮箱的重置链
url(r'^reset/(?P<active_code>.*)/$', ResetView.as_view(), name="reset_pwd"),
views.py
# 重置密码的view
class ResetView(View):
def get(self, request, active_code):
# 查询邮箱验证记录是否存在
all_record = EmailVerifyRecord.objects.filter(code=active_code)
# 如果不为空也就是有用户
active_form = ActiveForm(request.GET)
if all_record:
for record in all_record:
# 获取到对应的邮箱
email = record.email
# 将email传回来
return render(request, "password_reset.html", {"email": email})
# 自己瞎输的验证码
else:
return render(
request, "forgetpwd.html", {
"msg": "您的重置密码链接无效,请重新请求", "active_form": active_form})
forms.py
# 重置密码form实现
class ModifyPwdForm(forms.Form):
# 密码不能小于5位
password1 = forms.CharField(required=True, min_length=5)
# 密码不能小于5位
password2 = forms.CharField(required=True, min_length=5)
修改密码
view.py:
# 改变密码的view
class ModifyPwdView(View):
def post(self, request):
modiypwd_form = ModifyPwdForm(request.POST)
if modiypwd_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)
# save保存到数据库
user.save()
return render(request, "login.html", {"msg": "密码修改成功,请登录"})
# 验证失败说明密码位数不够。
else:
email = request.POST.get("email", "")
return render(
request, "password_reset.html", {
"email": email, "modiypwd_form": modiypwd_form})
urls.py
# 修改密码url; 用于passwordreset页面提交表
url(r'^modify_pwd/$', ModifyPwdView.as_view(), name="modify_pwd"),