You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Django/views.py

327 lines
12 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 导入日志模块
import logging
# 导入延迟翻译函数
from django.utils.translation import gettext_lazy as _
# 导入Django设置
from django.conf import settings
# 导入Django认证框架
from django.contrib import auth
# 导入重定向字段名常量
from django.contrib.auth import REDIRECT_FIELD_NAME
# 导入获取用户模型的函数
from django.contrib.auth import get_user_model
# 导入登出函数
from django.contrib.auth import logout
# 导入Django内置认证表单
from django.contrib.auth.forms import AuthenticationForm
# 导入密码哈希函数
from django.contrib.auth.hashers import make_password
# 导入HTTP响应重定向和禁止访问响应
from django.http import HttpResponseRedirect, HttpResponseForbidden
# 导入HTTP请求类型
from django.http.request import HttpRequest
# 导入HTTP响应类型
from django.http.response import HttpResponse
# 导入快捷函数获取对象或404错误
from django.shortcuts import get_object_or_404
# 导入快捷函数:渲染模板
from django.shortcuts import render
# 导入URL反向解析
from django.urls import reverse
# 导入方法装饰器
from django.utils.decorators import method_decorator
# 导入URL安全验证函数
from django.utils.http import url_has_allowed_host_and_scheme
# 导入基于类的视图基类
from django.views import View
# 导入禁止缓存装饰器
from django.views.decorators.cache import never_cache
# 导入CSRF保护装饰器
from django.views.decorators.csrf import csrf_protect
# 导入敏感参数保护装饰器
from django.views.decorators.debug import sensitive_post_parameters
# 导入通用视图类
from django.views.generic import FormView, RedirectView
# 导入项目工具函数
from djangoblog.utils import send_email, get_sha256, get_current_site, generate_code, delete_sidebar_cache
# 导入当前应用的工具函数
from . import utils
# 导入自定义表单
from .forms import RegisterForm, LoginForm, ForgetPasswordForm, ForgetPasswordCodeForm
# 导入用户模型
from .models import BlogUser
# 获取当前模块的日志器
logger = logging.getLogger(__name__)
# 在这里创建视图
class RegisterView(FormView):
"""用户注册视图"""
# 指定使用的表单类
form_class = RegisterForm
# 指定使用的模板
template_name = 'account/registration_form.html'
@method_decorator(csrf_protect) # CSRF保护装饰器
def dispatch(self, *args, **kwargs):
"""处理请求分发"""
# 调用父类的dispatch方法
return super(RegisterView, self).dispatch(*args, **kwargs)
def form_valid(self, form):
"""处理表单验证通过的情况"""
# 检查表单是否有效
if form.is_valid():
# 保存表单数据但不提交到数据库commit=False
user = form.save(False)
# 设置用户为未激活状态(需要邮箱验证)
user.is_active = False
# 设置用户来源为注册页面
user.source = 'Register'
# 保存用户到数据库
user.save(True)
# 获取当前站点域名
site = get_current_site().domain
# 生成验证签名:对密钥+用户ID进行双重SHA256哈希
sign = get_sha256(get_sha256(settings.SECRET_KEY + str(user.id)))
# 如果是调试模式,使用本地地址
if settings.DEBUG:
site = '127.0.0.1:8000'
# 获取结果页面的URL路径
path = reverse('account:result')
# 构建完整的验证URL
url = "http://{site}{path}?type=validation&id={id}&sign={sign}".format(
site=site, # 站点地址
path=path, # 结果页面路径
id=user.id, # 用户ID
sign=sign # 验证签名
)
# 构建邮件内容
content = """
<p>请点击下面链接验证您的邮箱</p>
<a href="{url}" rel="bookmark">{url}</a>
再次感谢您!
<br />
如果上面链接无法打开,请将此链接复制至浏览器。
{url}
""".format(url=url)
# 发送验证邮件
send_email(
emailto=[user.email], # 收件人邮箱
title='验证您的电子邮箱', # 邮件标题
content=content # 邮件内容
)
# 构建注册结果页面URL
url = reverse('accounts:result') + '?type=register&id=' + str(user.id)
# 重定向到结果页面
return HttpResponseRedirect(url)
else:
# 表单无效,重新渲染表单并显示错误
return self.render_to_response({'form': form})
class LogoutView(RedirectView):
"""用户登出视图"""
# 登出后重定向的URL
url = '/login/'
@method_decorator(never_cache) # 禁止缓存装饰器
def dispatch(self, request, *args, **kwargs):
"""处理请求分发"""
# 调用父类的dispatch方法
return super(LogoutView, self).dispatch(request, *args, **kwargs)
def get(self, request, *args, **kwargs):
"""处理GET请求登出操作"""
# 调用Django登出函数
logout(request)
# 删除侧边栏缓存
delete_sidebar_cache()
# 调用父类的get方法进行重定向
return super(LogoutView, self).get(request, *args, **kwargs)
class LoginView(FormView):
"""用户登录视图"""
# 指定使用的表单类
form_class = LoginForm
# 指定使用的模板
template_name = 'account/login.html'
# 登录成功后的重定向URL
success_url = '/'
# 重定向字段名
redirect_field_name = REDIRECT_FIELD_NAME
# 登录会话保持时间:一个月(秒数)
login_ttl = 2626560
# 方法装饰器保护敏感参数、CSRF保护、禁止缓存
@method_decorator(sensitive_post_parameters('password'))
@method_decorator(csrf_protect)
@method_decorator(never_cache)
def dispatch(self, request, *args, **kwargs):
"""处理请求分发"""
# 调用父类的dispatch方法
return super(LoginView, self).dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
"""获取模板上下文数据"""
# 从GET参数中获取重定向URL
redirect_to = self.request.GET.get(self.redirect_field_name)
# 如果重定向URL为空设置为首页
if redirect_to is None:
redirect_to = '/'
# 将重定向URL添加到上下文数据中
kwargs['redirect_to'] = redirect_to
# 调用父类方法获取基础上下文数据
return super(LoginView, self).get_context_data(**kwargs)
def form_valid(self, form):
"""处理表单验证通过的情况"""
# 创建认证表单实例使用POST数据
form = AuthenticationForm(data=self.request.POST, request=self.request)
# 检查表单是否有效
if form.is_valid():
# 删除侧边栏缓存
delete_sidebar_cache()
# 记录日志
logger.info(self.redirect_field_name)
# 登录用户
auth.login(self.request, form.get_user())
# 如果用户选择了"记住我"
if self.request.POST.get("remember"):
# 设置会话过期时间为一个月
self.request.session.set_expiry(self.login_ttl)
# 调用父类的form_valid方法会处理重定向
return super(LoginView, self).form_valid(form)
else:
# 表单无效,重新渲染表单并显示错误
return self.render_to_response({'form': form})
def get_success_url(self):
"""获取登录成功后的重定向URL"""
# 从POST数据中获取重定向URL
redirect_to = self.request.POST.get(self.redirect_field_name)
# 验证重定向URL是否安全同源策略
if not url_has_allowed_host_and_scheme(
url=redirect_to,
allowed_hosts=[self.request.get_host()]):
# 如果不安全使用默认的成功URL
redirect_to = self.success_url
# 返回重定向URL
return redirect_to
def account_result(request):
"""账户操作结果页面视图函数"""
# 从GET参数获取操作类型
type = request.GET.get('type')
# 从GET参数获取用户ID
id = request.GET.get('id')
# 根据ID获取用户对象如果不存在返回404
user = get_object_or_404(get_user_model(), id=id)
# 记录日志
logger.info(type)
# 如果用户已激活,重定向到首页
if user.is_active:
return HttpResponseRedirect('/')
# 检查操作类型是否为注册或验证
if type and type in ['register', 'validation']:
# 如果是注册操作
if type == 'register':
# 设置注册成功的内容和标题
content = '''
恭喜您注册成功,一封验证邮件已经发送到您的邮箱,请验证您的邮箱后登录本站。
'''
title = '注册成功'
else:
# 生成验证签名
c_sign = get_sha256(get_sha256(settings.SECRET_KEY + str(user.id)))
# 从GET参数获取签名
sign = request.GET.get('sign')
# 验证签名是否正确
if sign != c_sign:
# 签名错误,返回禁止访问
return HttpResponseForbidden()
# 激活用户账户
user.is_active = True
# 保存用户
user.save()
# 设置验证成功的内容和标题
content = '''
恭喜您已经成功的完成邮箱验证,您现在可以使用您的账号来登录本站。
'''
title = '验证成功'
# 渲染结果页面
return render(request, 'account/result.html', {
'title': title, # 页面标题
'content': content # 页面内容
})
else:
# 无效的操作类型,重定向到首页
return HttpResponseRedirect('/')
class ForgetPasswordView(FormView):
"""忘记密码重置视图"""
# 指定使用的表单类
form_class = ForgetPasswordForm
# 指定使用的模板
template_name = 'account/forget_password.html'
def form_valid(self, form):
"""处理表单验证通过的情况"""
# 检查表单是否有效
if form.is_valid():
# 根据邮箱获取用户对象
blog_user = BlogUser.objects.filter(
email=form.cleaned_data.get("email")
).get()
# 使用新密码的哈希值更新用户密码
blog_user.password = make_password(form.cleaned_data["new_password2"])
# 保存用户
blog_user.save()
# 重定向到登录页面
return HttpResponseRedirect('/login/')
else:
# 表单无效,重新渲染表单并显示错误
return self.render_to_response({'form': form})
class ForgetPasswordEmailCode(View):
"""获取忘记密码验证码的API视图"""
def post(self, request: HttpRequest):
"""处理POST请求"""
# 创建表单实例并验证数据
form = ForgetPasswordCodeForm(request.POST)
# 检查表单是否有效
if not form.is_valid():
# 表单无效,返回错误响应
return HttpResponse("错误的邮箱")
# 从已验证数据中获取邮箱
to_email = form.cleaned_data["email"]
# 生成验证码
code = generate_code()
# 发送验证邮件
utils.send_verify_email(to_email, code)
# 将验证码保存到缓存
utils.set_code(to_email, code)
# 返回成功响应
return HttpResponse("ok")