从0开发一个Django博客系统
Posted 黑马程序员官方
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从0开发一个Django博客系统相关的知识,希望对你有一定的参考价值。
目录
一、项目准备
1.1 项目分析
需求分析原因
- 项目中,需求驱动开发。即开发人员需要以需求为目标来实现业务逻辑。
需求分析方式
- 企业中,借助产品原型图分析需求。
- 需求分析完后,前端按照产品原型图开发前端页面,后端开发对应的业务及响应处理。
- 提示:我们现在假借示例网站作为原型图来分析需求。
需求分析内容
- 页面及页面上的业务逻辑。
- 归纳业务逻辑并划分模块。
1. 项目主要页面介绍
1.注册页面
2.登录页面
3.忘记密码页面
4.用户中心页面
5.写博客页面
6.博客首页
7.博客详情页面
2. 归纳项目主要模块
模块 | 功能 |
---|---|
注册 | 图形验证、短信验证 |
登录 | 状态保持、Cookie、Session |
个人中心 | 图片上传、更新数据 |
发布博客 | 数据入库 |
博客首页 | 数据分页 |
博客详情 | 博客详情数据展示、评论功能 |
3. 项目开发模式
选项 | 技术选型 |
---|---|
开发模式 | 前后端不分离 |
后端框架 | Django + Django模板引擎 |
1.2 工程创建和配置
1.2.1 工程创建和配置
1. 准备项目代码仓库
1. 源码托管网站
2. 创建源码远程仓库:itheima_blog
2. 克隆项目代码仓库
1.进入本地项目目录
cd Desktop/
2.克隆仓库
git clone https://github.com/qruihua/itheima_blog.git
3. 创建美多商城工程
1.进入本地项目仓库
cd itheima_blog/
2.创建美多商城虚拟环境,安装Django框架
$ mkvirtualenv -p python3 blog
$ pip install django==2.2
3.创建美多商城Django工程
$ django-admin startproject blog
创建工程完成后:运行程序,测试结果。
1.2.2 配置MySQL数据库
1. 新建MySQL数据库
1.新建MySQL数据库:blog
$ create database blog charset=utf8;
2.新建MySQL用户
$ create user itheima identified by '123456';
3.授权itheima用户访问blog数据库
$ grant all on blog.* to 'itheima'@'%';
4.授权结束后刷新特权
$ flush privileges;
2. 配置mysql数据库
DATABASES =
'default':
'ENGINE': 'django.db.backends.mysql', # 数据库引擎
'HOST': '127.0.0.1', # 数据库主机
'PORT': 3306, # 数据库端口
'USER': 'itheima', # 数据库用户名
'PASSWORD': '123456', # 数据库用户密码
'NAME': 'blog' # 数据库名字
,
文档 可能出现的错误
- Error loading MySQLdb module: No module named 'MySQLdb'.
出现错误的原因:
- Django中操作MySQL数据库需要驱动程序MySQLdb
- 目前项目虚拟环境中没有驱动程序MySQLdb
解决办法:
- 安装PyMySQL扩展包
- 因为MySQLdb只适用于Python2.x的版本,Python3.x的版本中使用PyMySQL替代MySQLdb
3. 安装PyMySQL扩展包
1.安装驱动程序
$ pip install PyMySQL
2.在工程同名子目录的__init__.py 文件中,添加如下代码:
import pymysql
pymysql.install_as_MySQLdb()
配置完成后:运行程序,测试结果。
1.2.3 配置Redis数据库
1. 安装django-redis扩展包
1.安装django-redis扩展包
$ pip install django-redis
2.django-redis使用说明文档
2. 配置Redis数据库
CACHES =
"default": # 默认
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379/0",
"OPTIONS":
"CLIENT_CLASS": "django_redis.client.DefaultClient",
,
"session": # session
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379/1",
"OPTIONS":
"CLIENT_CLASS": "django_redis.client.DefaultClient",
,
SESSION_ENGINE = "django.contrib.sessions.backends.cache"
SESSION_CACHE_ALIAS = "session"
default:
- 默认的Redis配置项,采用0号Redis库。
session:
- 状态保持的Redis配置项,采用1号Redis库。
SESSION_ENGINE
- 修改session存储机制使用Redis保存。
SESSION_CACHE_ALIAS:
- 使用名为"session"的Redis配置项存储session数据。
配置完成后:运行程序,测试结果。
1.2.4 配置工程日志
1. 配置工程日志
LOGGING =
'version': 1,
'disable_existing_loggers': False, # 是否禁用已经存在的日志器
'formatters': # 日志信息显示的格式
'verbose':
'format': '%(levelname)s %(asctime)s %(module)s %(lineno)d %(message)s'
,
'simple':
'format': '%(levelname)s %(module)s %(lineno)d %(message)s'
,
,
'filters': # 对日志进行过滤
'require_debug_true': # django在debug模式下才输出日志
'()': 'django.utils.log.RequireDebugTrue',
,
,
'handlers': # 日志处理方法
'console': # 向终端中输出日志
'level': 'INFO',
'filters': ['require_debug_true'],
'class': 'logging.StreamHandler',
'formatter': 'simple'
,
'file': # 向文件中输出日志
'level': 'INFO',
'class': 'logging.handlers.RotatingFileHandler',
'filename': os.path.join(BASE_DIR, 'logs/blog.log'), # 日志文件的位置
'maxBytes': 300 * 1024 * 1024,
'backupCount': 10,
'formatter': 'verbose'
,
,
'loggers': # 日志器
'django': # 定义了一个名为django的日志器
'handlers': ['console', 'file'], # 可以同时向终端与文件中输出日志
'propagate': True, # 是否继续传递日志信息
'level': 'INFO', # 日志器接收的最低日志级别
,
2. 准备日志文件目录
3. 日志记录器的使用
不同的应用程序所定义的日志等级可能会有所差别,分的详细点的会包含以下几个等级:
- FATAL/CRITICAL = 重大的,危险的
- ERROR = 错误
- WARNING = 警告
- INFO = 信息
- DEBUG = 调试
- NOTSET = 没有设置
import logging
# 创建日志记录器
logger = logging.getLogger('django')
# 输出日志
logger.debug('测试logging模块debug')
logger.info('测试logging模块info')
logger.error('测试logging模块error')
1.2.5 静态资源文件
1. 准备静态文件
2. 指定静态文件加载路径
STATIC_URL = '/static/'
# 配置静态文件加载路径
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]
配置完成后:运行程序,测试结果。
二、注册
2.1 界面展示
1. 创建用户模块应用
创建应用users
$ python manage.py startapp users
2. 注册用户模块应用
INSTALLED_APPS = [
...
'users.apps.UsersConfig',
]
注册完users应用后,运行测试程序。
3. 准备模板目录并设置模板路径
4. 定义用户注册视图
1.将static文件夹下在register.html拖拽到templates文件中
2.在users.views.py文件中定义视图
from django.views import View
class RegisterView(View):
"""用户注册"""
def get(self, request):
"""
提供注册界面
:param request: 请求对象
:return: 注册界面
"""
return render(request, 'register.html')
5. 定义用户注册路由
1.在users子应用中创建urls.py文件,并定义子路由
from django.urls import path
from users.views import RegisterView
urlpatterns = [
# 参数1:路由
# 参数2:视图函数
# 参数3:路由名,方便通过reverse来获取路由
path('register/',RegisterView.as_view(),name='register'),
]
2.在工程的urls.py总路由中添加子应用路由引导
from django.contrib import admin
from django.urls import path,include
urlpatterns = [
path('admin/', admin.site.urls),
# include 参数1要设置为元组(urlconf_module, app_name)
# namespace 设置命名空间
path('', include(('users.urls', 'users'), namespace='users')),
]
运行测试程序。
6.修改静态文件加载方式
1.是 由于静态资源加载是相对路径,因此我们需要修改静态资源的加载方式
以下代码是html的header处修改
% load staticfiles %
<!-- 引入bootstrap的css文件 -->
<link rel="stylesheet" href="% static 'bootstrap/css/bootstrap.min.css' %">
<!-- 引入vuejs -->
<script type="text/javascript" src="% static 'js/vue-2.5.16.js' %"></script>
<script type="text/javascript" src="% static 'js/axios-0.18.0.min.js' %"></script>
。。。
以下代码是html的footer处修改
<!-- 引入js -->
<script type="text/javascript" src="% static 'js/host.js' %"></script>
<script type="text/javascript" src="% static 'js/common.js' %"></script>
<script type="text/javascript" src="% static 'js/register.js' %"></script>
运行测试程序,没有问题
2.2 定义用户模型类
1. Django默认用户认证系统
- Django自带用户认证系统
- 它处理用户账号、组、权限以及基于cookie的用户会话。
- Django认证系统位置
- django.contrib.auth包含认证框架的核心和默认的模型。
- Django认证系统同时处理认证和授权
- 认证:验证一个用户是否它声称的那个人,可用于账号登录。
- 授权:授权决定一个通过了认证的用户被允许做什么。
- Django认证系统包含的内容
- 用户:用户模型类、用户认证。
- 权限:标识一个用户是否可以做一个特定的任务,MIS系统常用到。
- 组:对多个具有相同权限的用户进行统一管理,MIS系统常用到。
- 密码:一个可配置的密码哈希系统,设置密码、密码校验。
2. Django默认用户模型类
- Django认证系统中提供了用户模型类User保存用户的数据。
- User对象是认证系统的核心。
- Django认证系统用户模型类位置
- django.contrib.auth.models.User
父类AbstractUser介绍
- User对象基本属性
- 创建用户必选:username、password
- 创建用户可选:email、first_name、last_name、last_login、date_joined、is_active 、is_staff、is_superuse
- 判断用户是否通过认证:is_authenticated
- USERNAME_FIELD:可以修改用户名认证字段
- 创建用户的方法 user = User.objects.create_user(username, password, **extra_fields)
- 用户认证的方法 from django.contrib.auth import authenticate user = authenticate(username=username, password=password, **kwargs)
- 处理密码的方法
- 设置密码:set_password(raw_password)
- 校验密码:check_password(raw_password)
3. 自定义用户模型类
思考:为什么要自定义用户模型类?
- 观察注册界面会发现,个人博客注册页面中必须有手机号,而且在登录页面中也使用手机号进行认证。此外个人中心页面中有个人头像和个人简介字段。
- 但是Django默认用户模型类中没有这些字段,所以要自定义用户模型类。
如何自定义用户模型类?
- 继承自AbstractUser(可通过阅读Django默认用户模型类的源码得知) 。
- 新增手机号字段,头像字段和简介字段。
from django.db import models
from django.contrib.auth.models import AbstractUser
# 用户信息
class User(AbstractUser):
# 电话号码字段
# unique 为唯一性字段
mobile = models.CharField(max_length=20, unique=True,blank=True)
# 头像
# upload_to为保存到响应的子目录中
avatar = models.ImageField(upload_to='avatar/%Y%m%d/', blank=True)
# 个人简介
user_desc = models.TextField(max_length=500, blank=True)
# 修改认证的字段
USERNAME_FIELD = 'mobile'
#创建超级管理员的需要必须输入的字段
REQUIRED_FIELDS = ['username','email']
# 内部类 class Meta 用于给 model 定义元数据
class Meta:
db_table='tb_user' #修改默认的表名
verbose_name='用户信息' # Admin后台显示
verbose_name_plural=verbose_name # Admin后台显示
def __str__(self):
return self.mobile
4. 指定用户模型类
思考:为什么Django默认用户模型类是User?
- 阅读源代码:'django.conf.global_settings’ AUTH_USER_MODEL = 'auth.User'
结论:
- Django用户模型类是通过全局配置项AUTH_USER_MODEL决定的
配置规则: AUTH_USER_MODEL = '应用名.模型类名'
# 指定本项目用户模型类
AUTH_USER_MODEL = 'users.User'
5. 迁移用户模型类
1.创建迁移文件
- python manage.py makemigrations
2.执行迁移文件
- python manage.py migrate
运行测试程序
2.3 图形验证码
1. 准备captcha包(该包用于生成图形验证码)
1.将生成图片验证码的库复制到新建的libs包中。
2.安装Python处理图片的库 报错信息如下:
解决方案:在虚拟环境中安装Pillow。执行pip install Pillow即可
2. 图形验证码后端接口设计
1.请求方式
选项 | 方案 |
---|---|
请求方法 | GET |
请求地址 | imagecode?uuid=xxxxx-xxxx-xxxxxx |
2.请求参数:路径参数
参数名 | 类型 | 是否必传 | 说明 |
---|---|---|---|
uuid | string | 是 | 唯一编号 |
3.响应结果:image/jpeg
3. 图形验证码后端实现
1.图形验证码视图
from django.http import HttpResponseBadRequest,HttpResponse
from libs.captcha.captcha import captcha
from django_redis import get_redis_connection
class ImageCodeView(View):
def get(self,request):
#获取前端传递过来的参数
uuid=request.GET.get('uuid')
#判断参数是否为None
if uuid is None:
return HttpResponseBadRequest('请求参数错误')
# 获取验证码内容和验证码图片二进制数据
text, image = captcha.generate_captcha()
# 将图片验内容保存到redis中,并设置过期时间
redis_conn = get_redis_connection('default')
redis_conn.setex('img:%s' % uuid, 300, text)
# 返回响应,将生成的图片以content_type为image/jpeg的形式返回给请求
return HttpResponse(image, content_type='image/jpeg')
2.总路由
from django.contrib import admin
from django.urls import path,include
urlpatterns = [
path('admin/', admin.site.urls),
# include 参数1要设置为元组(urlconf_module, app_name)
# namespace 设置命名空间
path('', include(('users.urls', 'users'), namespace='users')),
]
3.子路由
from django.urls import path
from users.views import ImageCodeView
urlpatterns = [
# 参数1:路由
# 参数2:视图函数
# 参数3:路由名,方便通过reverse来获取路由
path('imagecode/', ImageCodeView.as_view(),name='imagecode'),
]
4. 修改模板中图片验证码HTML代码
1.html中的原代码如下
<img src="% static 'img/image_code.png' %" @click="generate_image_code" alt="" style="width: 110px;height: 40px;">
2.修改如下
<img :src="image_code_url" @click="generate_image_code" alt="" style="width: 110px;height: 40px;">
2.4 短信验证码
1. 容联云短信平台操作
1.容联云官网
- 容联云通讯网址:https://www.yuntongxun.com/
- 注册并登陆
2.容联云管理控制台
3 .添加容联云测试手机号
4.短信模板 免费开发测试使用的模板ID为1,形式为:【云通讯】您使用的是云通讯短信模板,您的验证码是1,请于2分钟内正确输入。
2. 短信发送测试
1.集成短信SDK到库中 CCPRestSDK.py:由容联云通讯开发者编写的官方SDK文件,包括发送模板短信的方法 ccp_sms.py:调用发送模板短信的方法
2.修改相应参数进行测试
3.进行测试
3. 短信验证码后端接口设计
1.请求方式
选项 | 方案 |
---|---|
请求方法 | GET |
请求地址 | /smscode/?mobile=xxxxℑ_code=xxxx&uuid=xxxxx |
2.请求参数:路径参数
参数名 | 类型 | 是否必传 | 说明 |
---|---|---|---|
mobile | string | 是 | 手机号 |
image_code | string | 是 | 图形验证码 |
uuid | string | 是 | 唯一编号 |
3.响应结果:JSON
字段 | 说明 |
---|---|
code | 状态码 |
errmsg | 错误信息 |
4. 短信验证码后端逻辑实现
from django.http import JsonResponse
from utils.response_code import RETCODE
from random import randint
from libs.yuntongxun.sms import CCP
import logging
logger=logging.getLogger('django')
class SmsCodeView(View):
def get(self,request):
# 接收参数
image_code_client = request.GET.get('image_code')
uuid = request.GET.get('uuid')
mobile=request.GET.get('mobile')
# 校验参数
if not all([image_code_client, uuid,mobile]):
return JsonResponse('code': RETCODE.NECESSARYPARAMERR, 'errmsg': '缺少必传参数')
# 创建连接到redis的对象
redis_conn = get_redis_connection('default')
# 提取图形验证码
image_code_server = redis_conn.get('img:%s' % uuid)
if image_code_server is None:
# 图形验证码过期或者不存在
return JsonResponse('code': RETCODE.IMAGECODEERR, 'errmsg': '图形验证码失效')
# 删除图形验证码,避免恶意测试图形验证码
try:
redis_conn.delete('img:%s' % uuid)
except Exception as e:
logger.error(e)
# 对比图形验证码
image_code_server = image_code_server.decode() # bytes转字符串
if image_code_client.lower() != image_code_server.lower(): # 转小写后比较
return JsonResponse('code': RETCODE.IMAGECODEERR, 'errmsg': '输入图形验证码有误')
# 生成短信验证码:生成6位数验证码
sms_code = '%06d' % randint(0, 999999)
#将验证码输出在控制台,以方便调试
logger.info(sms_code)
# 保存短信验证码到redis中,并设置有效期
redis_conn.setex('sms:%s' % mobile, 300, sms_code)
# 发送短信验证码
CCP().send_template_sms(mobile, [sms_code, 5],1)
# 响应结果
return JsonResponse('code': RETCODE.OK, 'errmsg': '发送短信成功')
5. 添加response_code文件
在工程中新建utils包,将response_code文件复制到utils中
2.5 用户注册实现
1. 用户注册接口设计
1.请求方式
选项 | 方案 |
---|---|
请求方法 | POST |
请求地址 | /register/ |
2.请求参数:表单参数
参数名 | 类型 | 是否必传 | 说明 |
---|---|---|---|
mobile | string | 是 | 用户名 |
password | string | 是 | 密码 |
password2 | string | 是 | 确认密码 |
sms_code | string | 是 | 短信验证码 |
3.响应结果:HTML
响应结果 | 响应内容 |
---|---|
注册失败 | 响应错误提示 |
注册成功 | 重定向到首页 |
2. 用户注册接口实现
1.注册视图
import re
from users.models import User
from django.db import DatabaseError
class RegisterView(View):
def post(self,request):
#接收参数
mobile = request.POST.get('mobile')
password = request.POST.get('password')
password2 = request.POST.get('password2')
smscode=request.POST.get('sms_code')
# 判断参数是否齐全
if not all([mobile, password, password2, smscode]):
return HttpResponseBadRequest('缺少必传参数')
# 判断手机号是否合法
if not re.match(r'^1[3-9]\\d9$', mobile):
return HttpResponseBadRequest('请输入正确的手机号码')
# 判断密码是否是8-20个数字
if not re.match(r'^[0-9A-Za-z]8,20$', password):
return HttpResponseBadRequest('请输入8-20位的密码')
# 判断两次密码是否一致
if password != password2:
return HttpResponseBadRequest('两次输入的密码不一致')
#验证短信验证码
redis_conn = get_redis_connection('default')
sms_code_server = redis_conn.get('sms:%s' % mobile)
if sms_code_server is None:
return HttpResponseBadRequest('短信验证码已过期')
if smscode != sms_code_server.decode():
return HttpResponseBadRequest('短信验证码错误')
# 保存注册数据
try:
user=User.objects.create_user(username=mobile,mobile=mobile, password=password)
except DatabaseError:
return HttpResponseBadRequest('注册失败')
# 响应注册结果
return HttpResponse('注册成功,重定向到首页')
2.在进行页面提交时报403csrf问题
3.在HTML表单中添加csrf_token
2.6 展示首页
1.创建首页子应用
- 重要提示:注册成功,重定向到首页
1.创建首页应用:home
$ python manage.py startapp home
2.定义首页视图:IndexView
from django.views import View
class IndexView(View):
"""首页广告"""
def get(self, request):
"""提供首页广告界面"""
return render(request, 'index.html')
3.配置首页路由
在home子应用中创建urls.py文件,并定义子路由
from django.urls import path
from home.views import IndexView
urlpatterns = [
path('', IndexView.as_view(),name='index'),
]
在工程的urls.py总路由中添加子应用路由引导
from django.urls import path, include
urlpatterns = [
path('', include(('home.urls','home'),namespace='home')),
]
4.测试首页是否可以正常访问
http://127.0.0.1:8000/
5.修改注册成功后,重定向到首页
from django.shortcuts import redirect
from django.urls import reverse
# 响应注册结果
return redirect(reverse('home:index'))
2.7 状态保持
说明:
- 如果需求是注册成功后即表示用户认证通过,那么此时可以在注册成功后实现状态保持
- 如果需求是注册成功后不表示用户认证通过,那么此时不用在注册成功后实现状态保持
当前的需求是:注册成功后即表示用户认证通过
1. login()方法介绍
- 状态保持:
- 将通过认证的用户的唯一标识信息(比如:用户ID)写入到当前session会话中
- login()方法:
- Django用户认证系统提供了login()方法
- 封装了写入session的操作,帮助我们快速实现状态保持
- login()位置:
- django.contrib.auth.__init__.py文件中 login(request, user)
2. login()方法使用
# 保存注册数据
try:
user=User.objects.create_user(username=mobile,mobile=mobile, password=password)
except DatabaseError:
return HttpResponseBadRequest('注册失败')
# 实现状态保持
login(request, user)
# 响应注册结果
return redirect(reverse('home:index'))
3. 查看状态保持结果
4. 设置首页所需的用户名信息和登录状态
1.设置cookie代码
# 保存注册数据
try:
user=User.objects.create_user(username=mobile,mobile=mobile, password=password)
except DatabaseError:
return HttpResponseBadRequest('注册失败')
# 实现状态保持
from django.contrib.auth import login
login(request, user)
#跳转到首页
response = redirect(reverse('home:index'))
#设置cookie
#登录状态,会话结束后自动过期
response.set_cookie('is_login',True)
#设置用户名有效期一个月
response.set_cookie('username',user.username,max_age=30*24*3600)
return response
2.查看cookie数据
三、登录
3.1 手机登录
1. 登录页面展示
1.在users.views.py文件中定义视图
from django.views import View
class LoginView(View):
def get(self,request):
return render(request,'login.html')
2 .在users.urls.py文件中定义路由
from users.views import LoginView
urlpatterns = [
# 参数1:路由
# 参数2:视图函数
# 参数3:路由名,方便通过reverse来获取路由
path('login/', LoginView.as_view(),name='login'),
]
3 .修改login.html中的资源加载方式
<!-- Header部分 -->
% load staticfiles %
<!-- 引入bootstrap的css文件 -->
<link rel="stylesheet" href="% static 'bootstrap/css/bootstrap.min.css' %">
<!-- 引入vuejs -->
<script type="text/javascript" src="% static 'js/vue-2.5.16.js' %"></script>
<script type="text/javascript" src="% static 'js/axios-0.18.0.min.js' %"></script>
...
<!-- Footer部分 -->
<script type="text/javascript" src="% static 'js/host.js' %"></script>
<script type="text/javascript" src="% static 'js/common.js' %"></script>
<script type="text/javascript" src="% static 'js/login.js' %"></script>
...
<!-- 点击注册部分 -->
<small class="form-text text-muted ml-1">还没有账号?<a href="% url 'users:register' %" style="color: cornflowerblue; ">注册新账号</a>
2. 登录接口设计
1.请求方式
选项 | 方案 |
---|---|
请求方法 | POST |
请求地址 | /login/ |
2.请求参数:表单
参数名 | 类型 | 是否必传 | 说明 |
---|---|---|---|
username | string | 是 | 用户名 |
password | string | 是 | 密码 |
remember | string | 否 | 是否记住用户 |
3.响应结果:HTML
字段 | 说明 |
---|---|
登录失败 | 响应错误提示 |
登录成功 | 重定向到首页 |
3. 登录接口实现
from django.contrib.auth import login
from django.contrib.auth import authenticate
class LoginView(View):
def post(self,request):
# 接受参数
mobile = request.POST.get('mobile')
password = request.POST.get('password')
remember = request.POST.get('remember')
# 校验参数
# 判断参数是否齐全
if not all([mobile, password]):
return HttpResponseBadRequest('缺少必传参数')
# 判断手机号是否正确
if not re.match(r'^1[3-9]\\d9$', mobile):
return HttpResponseBadRequest('请输入正确的手机号')
# 判断密码是否是8-20个数字
if not re.match(r'^[0-9A-Za-z]8,20$', password):
return HttpResponseBadRequest('密码最少8位,最长20位')
# 认证登录用户
# 认证字段已经在User模型中的USERNAME_FIELD = 'mobile'修改
user=authenticate(mobile=mobile, password=password)
if user is None:
return HttpResponseBadRequest('用户名或密码错误')
# 实现状态保持
login(request, user)
# 响应登录结果
response = redirect(reverse('home:index'))
# 设置状态保持的周期
if remember != 'on':
# 没有记住用户:浏览器会话结束就过期
request.session.set_expiry(0)
# 设置cookie
response.set_cookie('is_login', True)
response.set_cookie('username', user.username, max_age=30 * 24 * 3600)
else:
# 记住用户:None表示两周后过期
request.session.set_expiry(None)
# 设置cookie
response.set_cookie('is_login', True, max_age=14*24 * 3600)
response.set_cookie('username', user.username, max_age=30 * 24 * 3600)
#返回响应
return response
注意: 默认的认证方法中是对username进行认证。我们需要修改认证的字段为mobile。所以我们需要在User的模型中修改 USERNAME_FIELD='mobile'才能实现手机号的认证
3.2 首页用户名展示
1. 用户名写入到cookie
# 响应登录结果
response = redirect(reverse('home:index'))
# 设置状态保持的周期
if remember != 'on':
# 没有记住用户:浏览器会话结束就过期
request.session.set_expiry(0)
# 设置cookie
response.set_cookie('is_login', True)
response.set_cookie('username', user.username, max_age=30 * 24 * 3600)
else:
# 记住用户:None表示两周后过期
request.session.set_expiry(None)
# 设置cookie
response.set_cookie('is_login', True, max_age=14*24 * 3600)
response.set_cookie('username', user.username, max_age=30 * 24 * 3600)
#返回响应
return response
2. Vue渲染首页用户名
1.index.html
<!-- 如果用户已经登录,则显示用户名下拉框 -->
<li class="nav-item dropdown" v-if="is_login">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" @click="show_menu_click">[[username]]</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown" style="display: block" v-show="show_menu">
<a class="dropdown-item" href="../static/write_blog.html">写文章</a>
<a class="dropdown-item" href='../static/center.html'>个人信息</a>
<a class="dropdown-item" href='#'>退出登录</a>
</div>
</li>
<!-- 如果用户未登录,则显示登录按钮 -->
<li class="nav-item" v-else>
<a class="nav-link" href="login.html">登录</a>
</li>
2.index.js
mounted()
//获取用户名信息
this.username=getCookie('username');
//获取是否登录信息
this.is_login=getCookie('is_login');
,
3.3 退出登录
1. logout()方法介绍
- 退出登录:
- 回顾登录:将通过认证的用户的唯一标识信息,写入到当前session会话中
- 退出登录:正好和登录相反(清理session会话信息)
- logout()方法:
- Django用户认证系统提供了logout()方法
- 封装了清理session的操作,帮助我们快速实现登出一个用户
logout()位置:
- django.contrib.auth.__init__.py文件中
logout(request)
2. logout()方法使用
from django.contrib.auth import logout
class LogoutView(View):
def get(self,request):
# 清理session
logout(request)
# 退出登录,重定向到登录页
response = redirect(reverse('home:index'))
# 退出登录时清除cookie中的登录状态
response.delete_cookie('is_login')
return response
提示:
- 由于首页中登录状态是从cookie中读取的。
- 所以退出登录时,需要将cookie中登录状态清除。
3.实现退出登录
<div class="dropdown-menu" aria-labelledby="navbarDropdown" style="display: block" v-show="show_menu">
<a class="dropdown-item" href="../static/write_blog.html">写文章</a>
<a class="dropdown-item" href='../static/center.html'>个人信息</a>
<a class="dropdown-item" href='% url 'users:logout' %'>退出登录</a>
</div>
四、忘记密码
1. 忘记密码页面展示
1.在users.views.py文件中定义视图
from django.views import View
class ForgetPasswordView(View):
def get(self, request):
return render(request, 'forget_password.html')
2 .在users.urls.py文件中定义路由
from users.views import ForgetPasswordView
urlpatterns = [
# 参数1:路由
# 参数2:视图函数
# 参数3:路由名,方便通过reverse来获取路由
path('forgetpassword/', ForgetPasswordView.as_view(),name='forgetpassword'),
]
3 .修改forget_password.html中的资源加载方式
<!-- Header部分 -->
% load staticfiles %
<!-- 引入bootstrap的css文件 -->
<link rel="stylesheet" href="% static 'bootstrap/css/bootstrap.min.css' %">
<!-- 引入vuejs -->
<script type="text/javascript" src="% static 'js/vue-2.5.16.js' %"></script>
<script type="text/javascript" src="% static 'js/axios-0.18.0.min.js' %"></script>
...
<!-- Footer部分 -->
<script type="text/javascript" src="% static 'js/host.js' %"></script>
<script type="text/javascript" src="% static 'js/common.js' %"></script>
<script type="text/javascript" src="% static 'js/forget_password.js' %"></script>
...
<!-- 图片验证码部分 -->
<img :src="image_code_url" @click="generate_image_code" alt="" style="width: 110px;height: 40px;">
4 .修改login.html中的忘记密码的跳转连接
<small class="form-text text-muted ml-1"><a class="secondaryAction layui-text" href="% url 'users:forgetpassword' %">忘记密码?</a>
2. 忘记密码接口设计
1.请求方式
选项 | 方案 |
---|---|
请求方法 | POST |
请求地址 | /forgetpassword/ |
2.请求参数:表单
参数名 | 类型 | 是否必传 | 说明 |
---|---|---|---|
mobile | string | 是 | 用户名 |
password | string | 是 | 密码 |
password2 | string | 是 | 确认密码 |
sms_code | string | 是 | 短信验证码 |
3.响应结果:HTML
字段 | 说明 |
---|---|
修改失败 | 响应错误提示 |
修改成功 | 重定向到登录 |
3. 忘记密码接口实现
class ForgetPasswordView(View):
def post(self, request):
# 接收参数
mobile = request.POST.get('mobile')
password = request.POST.get('password')
password2 = request.POST.get('password2')
smscode = request.POST.get('sms_code')
# 判断参数是否齐全
if not all([mobile, password, password2, smscode]):
return HttpResponseBadRequest('缺少必传参数')
# 判断手机号是否合法
if not re.match(r'^1[3-9]\\d9$', mobile):
return HttpResponseBadRequest('请输入正确的手机号码')
# 判断密码是否是8-20个数字
if not re.match(r'^[0-9A-Za-z]8,20$', password):
return HttpResponseBadRequest('请输入8-20位的密码')
# 判断两次密码是否一致
if password != password2:
return HttpResponseBadRequest('两次输入的密码不一致')
# 验证短信验证码
redis_conn = get_redis_connection('default')
sms_code_server = redis_conn.get('sms:%s' % mobile)
if sms_code_server is None:
return HttpResponseBadRequest('短信验证码已过期')
if smscode != sms_code_server.decode():
return HttpResponseBadRequest('短信验证码错误')
# 根据手机号查询数据
try:
user = User.objects.get(mobile=mobile)
except User.DoesNotExist:
# 如果该手机号不存在,则注册个新用户
try:
User.objects.create_user(username=mobile, mobile=mobile, password=password)
except Exception:
return HttpResponseBadRequest('修改失败,请稍后再试')
else:
# 修改用户密码
user.set_password(password)
user.save()
# 跳转到登录页面
response = redirect(reverse('users:login'))
return response
五、用户中心
5.1 用户中心展示
1. 页面展示
1.在users.views.py文件中定义视图
from django.views import View
class UserCenterView(View):
def get(self,request):
return render(request,'center.html')
2 .在users.urls.py文件中定义路由
from users.views import UserCenterView
urlpatterns = [
# 参数1:路由
# 参数2:视图函数
# 参数3:路由名,方便通过reverse来获取路由
path('center/', UserCenterView.as_view(),name='center'),
]
3 .修改center.html中的资源加载方式
<!-- Header部分 -->
% load staticfiles %
<!-- 引入bootstrap的css文件 -->
<link rel="stylesheet" href="% static 'bootstrap/css/bootstrap.min.css' %">
<!-- 引入vuejs -->
<script type="text/javascript" src="% static 'js/vue-2.5.16.js' %"></script>
<script type="text/javascript" src="% static 'js/axios-0.18.0.min.js' %"></script>
...
<!-- Footer部分 -->
<script type="text/javascript" src="% static 'js/host.js' %"></script>
<script type="text/javascript" src="% static 'js/common.js' %"></script>
<script type="text/javascript" src="% static 'js/center.js' %"></script>
...
<!-- 页面跳转部分 -->
<a class="dropdown-item" href='% url 'users:center' %'>个人信息</a>
<a class="dropdown-item" href='% url 'users:logout' %'>退出登录</a>
4 .修改index.html中的的跳转连接
<a class="dropdown-item" href='% url 'users:center' %'>个人信息</a>
2. 判断用户是否登录
需求:
- 当用户登录后,才能访问用户中心。
- 如果用户未登录,就不允许访问用户中心,将用户引导到登录界面。
实现方案:
- 需要判断用户是否登录。
- 根据是否登录的结果,决定用户是否可以访问用户中心。
Django用户认证系统提供了方法
- request.user.is_authenticated()来判断用户是否登录。如果通过登录验证则返回True。反之,返回False。
- LoginRequiredMixin封装了判断用户是否登录的操作。
1.用户中心使用LoginRequiredMixin
from django.views import View
from django.contrib.auth.mixins import LoginRequiredMixin
class UserCenterView(LoginRequiredMixin,View):
def get(self,request):
return render(request,'center.html')
2.设置未登录用户跳转的路由
#在工程的settings.py文件中,添加以下配置。
LOGIN_URL = '/login/'
3.根据登录的next参数设置登录跳转路由
# 实现状态保持
login(request, user)
# 响应登录结果
next = request.GET.get('next')
if next:
response= redirect(next)
else:
response = redirect(reverse('home:index'))
3. 获取用户信息,模板渲染数据
1.获取用户信息修改center.html中的数据显示
from django.contrib.auth.mixins import LoginRequiredMixin
class UserCenterView(LoginRequiredMixin,View):
def get(self,request):
# 获取用户信息
user = request.user
#组织模板渲染数据
context =
'username': user.username,
'mobile': user.mobile,
'avatar': user.avatar.url if user.avatar else None,
'user_desc': user.user_desc
return render(request,'center.html',context=context)
2.修改center.html中的数据显示
<form method="post" enctype="multipart/form-data">
<!-- username -->
<div class="form-group col-md-4">
<label for="username">用户名</label>
<input type="text" class="form-control" id="username" name="username" value=" username " >
</div>
% if avatar %
<br> <div class="col-md-4">头像</div>
<img src=" avatar " style="max-width: 20%; border-radius: 15%;" class="col-md-4"><br>
% else %
<br><h5 class="col-md-4">暂无头像</h5><br>
% endif %
<!-- avatar -->
<div class="form-group col-md-4">
<label for="avatar">上传头像</label>
<input type="file" class="form-control-file" name="avatar" id="avatar">
</div>
<!-- phone -->
<div class="form-group col-md-4">
<label for="phone">电话</label>
<input type="text" class="form-control" disabled="disabled" id="phone" name="phone" value=" mobile ">
</div>
<!-- desc -->
<div class="form-group col-md-4">
以上是关于从0开发一个Django博客系统的主要内容,如果未能解决你的问题,请参考以下文章
网站开发学习Python实现-Django项目部署-同步之前写的博客(6.2.2)