当前位置:主页 > 查看内容

django搭建个人博客(一)

发布时间:2021-09-19 00:00| 位朋友查看

简介:准备阶段 首先熟悉项目的根本流程下面是对项目的每个模块流程阶段具体分析 模块 功能 注册 图形验证、短信验证 登陆 状态保存、cookie、session 个人中心 图片上传、更新数据 发布博客 数据入库 博客首页 数据分页 博客详情 博客详情数据展示、评论工程 总体……

准备阶段

首先熟悉项目的根本流程,下面是对项目的每个模块流程阶段具体分析

模块功能
注册图形验证、短信验证
登陆状态保存、cookie、session
个人中心图片上传、更新数据
发布博客数据入库
博客首页数据分页
博客详情博客详情数据展示、评论工程

总体流程介绍完毕开始做准备工作需要数据库来存储说用到的数据,根据数据字典创建对象的表格

文件所使用的静态资源和一些配置以提供,方便大家使用在这里插入图片描述

创建py项目

使用pycharm软件创建一个django项目
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

项目结构分析:

  1. manange.py:以后和项目交互基本上都是基于这个文件。一般都是在终端输入python manage.py [子命令]。可以输入python manage.py help看下能做什么事情。除非你知道你自己在做什么,一般情况下不应该编辑这个文件。
  2. settings.py:保存项目所有的配置信息。
  3. urls.py:用来做url与视图函数映射的。以后来了一个请求,就会从这个文件中找到匹配的视图函数。
  4. wsig.py:专门用来做部署的。不需要修改。
    项目创建完之后 可以运行 启动项目

可能会出现的坑
在这里插入图片描述
该问题就是把‘/’识别为除号了,两个str无法进行除号。代码实际意思是将两个str进行拼接,进入settings.py进行如下修改:

后面有具体配置文件

  1. ‘DIRS‘: [BASE_DIR / ‘templates‘] TypeError: unsupported operand type(s) for /: ‘str‘ and ‘str‘
    更改地方
    在这里插入图片描述
    2.可能会有此问题query = query.decode(errors=‘replace‘)

在这里插入图片描述

项目运行成功显示界面
在这里插入图片描述

准备阶段

配置数据库MySQL

打开本地数据库–> 用户自行选择,可以创建一个新的用户也可以使用root

新建数据库

create database blog charset=utf8;

创建新用户或者使用root用户

create user diangen identified by '123456';

授权新用户的权限

grant all on blog.* to 'diangen';

授权结束后刷新特权

 flush privileges;

数据库创建完毕

创建一个django项目找到配置文件里面的settings.py 连接数据库
在这里插入图片描述

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

配置并使用数据库
在这里插入图片描述

配置Redis数据库

检查并安装django-redis拓展包

  pip install django-redis

settings.py文件夹中添加

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数据。 配置完成后:运行程序,测试结果
电脑安装redis并进入redis文件目录
在命令行中运行
在这里插入图片描述

redis-server

在这里插入图片描述

redis运行成功

如果运行报错 看看是不是redis的问题
例如
问题原因是Redis 快照关闭了导致不能存储,可以通过关闭stop-writes-on-bgsave-error配置来解决。

(1)Windows系统中找到了redis.windows.conf文件,可以看到如下

# By default Redis will stop accepting writes if RDB snapshots are enabled
# (at least one save point) and the latest background save failed.
# This will make the user aware (in a hard way) that data is not persisting
# on disk properly, otherwise chances are that no one will notice and some
# disaster will happen.
#
# If the background saving process will start working again Redis will
# automatically allow writes again.
#
# However if you have setup your proper monitoring of the Redis server
# and persistence, you may want to disable this feature so that Redis will
# continue to work as usual even if there are problems with disk,
# permissions, and so forth.

stop-writes-on-bgsave-error yes

默认该设置是打开的,可以直接在此处修改为no

stop-writes-on-bgsave-error no

配置日志

settings.py文件夹中添加(后期报错方便在日志查看)

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',  # 日志器接收的最低日志级别
        },
    }
}

创建文件夹
在这里插入图片描述
不同的应用程序所定义的日志等级可能会有所差别,分的详细点的会包含以下几个等级:

FATAL/CRITICAL = 重大的,危险的
ERROR = 错误
WARNING = 警告
INFO = 信息
DEBUG = 调试
NOTSET = 没有设置

urls.py文件中

创建日志记录器

import logging
logger = logging.getLogger('django')

输出日志

logger.debug('测试logging模块debug')
logger.info('测试logging模块info')
logger.error('测试logging模块error')

配置静态资源

项目文件夹下创建目录static文件
settings.py 文件中添加
指定静态文件加载路径

STATIC_URL = '/static/'

配置静态文件加载路径

STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]

在这里插入图片描述

配置完成后:运行程序,测试结果。
在这里插入图片描述
在这里插入图片描述
项目启动成功
下面开始我们的具体的模块与应用

创建用户模块应用—注册

创建应用users

python manage.py startapp users

在这里插入图片描述
创建完之后目录结构会出现一个文件夹
在这里插入图片描述

在工程的setting.py中 注册用户模块应用
在这里插入图片描述

定义用户注册视图

创建一个子目录 把注册的html放进去 并更改配置
将static文件夹下在register.html拖拽到templates文件中
设置模板路径
在这里插入图片描述
在users.views.py文件中定义视图

from django.views import View

class RegisterView(View):
    """用户注册"""

    def get(self, request):
        """
        提供注册界面
        :param request: 请求对象
        :return: 注册界面
        """
        return render(request, 'register.html')

定义用户注册路由

在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'),
]

在工程的urls.py总路由中添加子应用路由引导 dblog中的urls

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')),
]

需要把static中的界面拖拽到templates文件夹中 因为我们配置的路径是template存放的界面
在这里插入图片描述
运行测试程序。
以上步骤是为了完成 第一个注册见面并且配置路由
首先配置user里面的视频viwes 和路由,然后在设置工程的urls.py总路由中添加子应用路由引导
在这里插入图片描述

修改静态文件加载方式

是由于静态资源加载是相对路径,因此我们需要修改静态资源的加载方式

使用static标签来加载静态文件。要使用static标签,首先需要{% load static %}

# 以下代码是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>
# 运行测试程序,没有问题

定义用户模型类

自定义用户模型类

思考:为什么要自定义用户模型类?
观察注册界面会发现,个人博客注册页面中必须有手机号,而且在登录页面中也使用手机号进行认证。此外个人中心页面中有个人头像和个人简介字段。
说白了就是用户需要自己增加字段to

如何自定义用户模型类?

继承自AbstractUser(可通过阅读Django默认用户模型类的源码得知) 。
新增手机号字段,头像字段和简介字段

这个基类仅有少部分由于Django机制,而设定的函数和常量。

如果连这个都不想继承的话,直接用自定义,这将有可能导致其余的Django组件无法正常工作。

当然无论是继承还是自定义,都必须在settings中设置AUTH_USER_MODEL

用户信息

from django.db import models

# Create your models here.
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

指定本项目用户模型类

AUTH_USER_MODEL = 'users.User'

在这里插入图片描述

迁移用户模型类

在这里插入图片描述
执行迁移文件 python manage.py migrate
在这里插入图片描述
运行测试程序

图形验证码接口设计和定义

准备captcha包(该包用于生成图形验证码)

将生成图片验证码的库复制到新建的libs包中。
在这里插入图片描述
安装Python处理图片的库
在这里插入图片描述

图形验证码后端接口设计

1.请求方式

选项方案
请求方法GET
请求地址imagecode?uuid=xxxxx-xxxx-xxxxxx

2.请求参数:路径参数

参数名 类型 是否必传 说明
uuid string 是 唯一编号
3.响应结果:image/jpeg

图形验证码后端实现
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'),
]

修改模板中图片验证码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;">

路由配置成功在注册界面可以正常显示验证码并切换

短信验证码

短信验证码 使用的是容联云官网 注册 获取
在这里插入图片描述
在这里插入图片描述

1.集成短信SDK到库中

CCPRestSDK.py:由容联云通讯开发者编写的官方SDK文件,包括发送模板短信的方法

ccp_sms.py:调用发送模板短信的方法

在这里插入图片描述

短信验证码后端逻辑实现

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': '发送短信成功'})

当点击验证码的时候 pycharm控制台中也会发送一个
在这里插入图片描述

添加response_code文件

在工程中新建utils包,将response_code文件复制到utils中
在这里插入图片描述

在这里插入图片描述

用户注册实现

from users.models import User
from django.db import DatabaseError
#注册视图
class RegisterView(View):

    def get(self,request):

        return render(request,'register.html')

    def post(self,request):
        """
        1.接收数据
        2.验证数据
            2.1 参数是否齐全
            2.2 手机号的格式是否正确
            2.3 密码是否符合格式
            2.4 密码和确认密码要一致
            2.5 短信验证码是否和redis中的一致
        3.保存注册信息
        4.返回响应跳转到指定页面
        :param request:
        :return:
        """
        # 1.接收数据
        mobile=request.POST.get('mobile')
        password=request.POST.get('password')
        password2=request.POST.get('password2')
        smscode=request.POST.get('sms_code')
        # 2.验证数据
        #     2.1 参数是否齐全
        if not all([mobile,password,password2,smscode]):
            return HttpResponseBadRequest('缺少必要的参数')
        #     2.2 手机号的格式是否正确
        if not re.match(r'^1[3-9]\d{9}$',mobile):
            return HttpResponseBadRequest('手机号不符合规则')
        #     2.3 密码是否符合格式
        if not re.match(r'^[0-9A-Za-z]{8,20}$',password):
            return HttpResponseBadRequest('请输入8-20位密码,密码是数字,字母')
        #     2.4 密码和确认密码要一致
        if password != password2:
            return HttpResponseBadRequest('两次密码不一致')
        #     2.5 短信验证码是否和redis中的一致
        redis_conn = get_redis_connection('default')
        redis_sms_code=redis_conn.get('sms:%s'%mobile)
        if redis_sms_code is None:
            return HttpResponseBadRequest('短信验证码已过期')
        if smscode != redis_sms_code.decode():
            return HttpResponseBadRequest('短信验证码不一致')
        # 3.保存注册信息
        # create_user 可以使用系统的方法来对密码进行加密
        try:
            user=User.objects.create_user(username=mobile,
                                      mobile=mobile,
                                      password=password)
        except DatabaseError as e:
            logger.error(e)
            return HttpResponseBadRequest('注册失败')

        from django.contrib.auth import login
        login(request,user)
        # 4.返回响应跳转到指定页面
        # 暂时返回一个注册成功的信息,后期再实现跳转到指定页面

        # redirect 是进行重定向
        # reverse 是可以通过 namespace:name 来获取到视图所对应的路由
        response = redirect(reverse('home:index'))
        # return HttpResponse('注册成功,重定向到首页')

        #设置cookie信息,以方便首页中 用户信息展示的判断和用户信息的展示
        response.set_cookie('is_login',True)
        response.set_cookie('username',user.username,max_age=7*24*3600)

        return response

在HTML表单中添加csrf_token
在这里插入图片描述
在这里插入图片描述

首页展示

1.创建首页应用:home

python manage.py startapp home

2.定义首页视图:IndexView—查询分类数据并展示
2.1.请求方式

选项方案
请求方法GET
请求地址/?cat_id=xxx&page_num=xxx&page_size=xxx

2.2.请求参数

参数名类型是否必传说明
cat_idstring分类id
page_numstring文章分页页码
page_sizestring文章每页条目数

2.3.响应结果:HTML

字段说明
失败响应错误提示
成功展示数据

3.查询分类文章数据并通过context传递给HTML

from django.urls import reverse
from django.views import View
# Create your views here.

class IndexView(View):
    """首页广告"""

    def get(self, request):
        """提供首页广告界面"""
        return render(request, 'index.html')

4.配置首页路由

在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')),
]

5.重定注册界面的跳转到首页

# 响应注册结果
return redirect(reverse('home:index'))

用户登陆

  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>

登录接口实现

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]\d{9}$', 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

注册登陆设置成功之后现在开始对首页进行设置用户名显示到首页
在这里插入图片描述

首页用户名展示

用户名写入到cookie
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');
},

在这里插入图片描述

退出登录

logout()方法介绍
退出登录:

对session操作的,也就是操作redis,所以返回的要么是空,要么成功,不会出现异常

logout()方法:

只需要传入一个request对象就行,就会把当前用户的session清除

logout()位置:

django.contrib.auth.init.py文件中

from django.contrib.auth import logout
class LogoutView(View):

    def get(self,request):
        # 1.session数据清除
        logout(request)
        # 2.删除部分cookie数据
        response=redirect(reverse('home:index'))
        response.delete_cookie('is_login')
        #3.跳转到首页
        return response

提示:

由于首页中登录状态是从cookie中读取的。
所以退出登录时,需要将cookie中登录状态清除。

实现退出登录

<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>

路由配置 users.urls.py

urlpatterns = [
    # 参数1:路由
    # 参数2:视图函数
    # 参数3:路由名,方便通过reverse来获取路由
    path('register/',RegisterView.as_view(),name='register'),
    path('imagecode/', ImageCodeView.as_view(),name='imagecode'),
    # 短信发送
    path('smscode/', SmsCodeView.as_view(), name='smscode'),
    # 登录路由
    path('login/', LoginView.as_view(), name='login'),
    # 退出登录
    path('logout/', LogoutView.as_view(), name='logout'),
]

忘记密码

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
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>

忘记密码接口实现

参数名类型是否必传说明
mobilestring用户名
passwordstring密码
password2string确认密码
sms_codestring短信验证码

代码

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]\d{9}$', 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

路由配置 urts.py

 # 忘记密码
    path('forgetpassword/', ForgetPasswordView.as_view(),name='forgetpassword'),

在这里插入图片描述

用户中心展示

  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>

在这里插入图片描述

判断用户是否登录

根据是否登录的结果,决定用户是否可以访问用户中心。
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'))

1.获取用户信息,模板渲染数据users.views.py 中添加功能

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">
        <label for="desc">简介</label>
        <!-- 文本区域 -->
        <textarea type="text" class="form-control" id="desc" name="desc" rows="12" >{{ user_desc }}</textarea>
    </div>
    <!-- 提交按钮 -->
    <button type="submit" class="btn btn-primary" style="margin-left: 12px" >修改</button>
</form>

用户中心修改

  1. 用户中心接口设计
    1.请求方式
选项方案
请求方法POST
请求地址/center/

2.请求参数:表单

参数名类型是否必传说明
username string用户名
avatarfile头像
descstring个人简介

3.响应结果:HTML

字段说明
修改失败响应错误提示
修改成功刷新展示
  1. 用户中心修改接口实现
from django.contrib.auth.mixins import LoginRequiredMixin
class UserCenterView(LoginRequiredMixin,View):

    def post(self,request):
        # 接收数据
        user = request.user
        avatar = request.FILES.get('avatar')
        username = request.POST.get('username',user.username)
        user_desc = request.POST.get('desc',user.user_desc)

        # 修改数据库数据
        try:
            user.username=username
            user.user_desc=user_desc
            if avatar:
                user.avatar=avatar
            user.save()
        except Exception as e:
            logger.error(e)
            return HttpResponseBadRequest('更新失败,请稍后再试')

        # 返回响应,刷新页面
        response = redirect(reverse('users:center'))
        #更新cookie信息
        response.set_cookie('username',user.username,max_age=30*24*3600)
        return response
  1. 用户中心头像的上传和展示
    1.在settings.py文件中设置图片上传的路径并新建文件夹media
MEDIA_ROOT = os.path.join(BASE_DIR, 'media/')

在settings.py文件中设置

图片的统一路由

MEDIA_URL = '/media/'

设置路由匹配规则。在工程的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')),
    path('', include(('home.urls','home'),namespace='home')),
]
#以下代码为设置图片访问路由规则
from django.conf import settings
from django.conf.urls.static import static
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

在这里插入图片描述

写博客页面展示

  1. 页面展示
    1.在users.views.py文件中定义视图
from django.views import View

class WriteBlogView(LoginRequiredMixin,View):

    def get(self,request):

        return render(request,'write_blog.html')

2.在users.urls.py文件中定义路由

from users.views import WriteBlogView
urlpatterns = [
    # 参数1:路由
    # 参数2:视图函数
    # 参数3:路由名,方便通过reverse来获取路由
    path('writeblog/', WriteBlogView.as_view(),name='writeblog'),
]

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部分 -->
<!--ckeditor-->
<script type="text/javascript" src="{% static 'ckeditor/ckeditor-init.js' %}" data-ckeditor-basepath="{% static 'ckeditor/ckeditor/' %}" id="ckeditor-init-script"></script>
<script type="text/javascript" src="{% static 'ckeditor/ckeditor/ckeditor.js' %}"></script>
<!-- 引入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/write_blog.js' %}"></script>
<!-- 页面跳转部分 -->
<a class="dropdown-item" href="{% url 'users:writeblog' %}">写文章</a>
<a class="dropdown-item" href='{% url 'users:center'%}'>个人信息</a>
<a class="dropdown-item" href='{% url 'users:center' %}'>退出登录</a>

4.修改index.html中的的跳转连接

<a class="dropdown-item" href="{% url 'users:writeblog' %}">写文章</a>

在这里插入图片描述

文章分类模型

  1. 定义模型类
    在home子应用的models.py模型中定义文章分类模型
from django.db import models
from django.utils import timezone

class ArticleCategory(models.Model):
    """
    文章分类
    """
    # 栏目标题
    title = models.CharField(max_length=100, blank=True)
    # 创建时间
    created = models.DateTimeField(default=timezone.now)

    def __str__(self):
        return self.title

    class Meta:
        db_table='tb_category'
        verbose_name = '类别管理'
        verbose_name_plural = verbose_name
  1. 迁移模型类
1.创建迁移文件

python manage.py makemigrations
2.执行迁移文件

python manage.py migrate

文章分类后台管理

当刚创建的时候,首页是没有数据,我们需要从后台管理界面进行文章分类,从而展示到主界面
网站的管理员负责查看、添加、修改、删除数据

Django能够根据定义的模型类自动地生成管理模块
登陆站点:http://127.0.0.1:8000/admin
在setting中设置中文信息

LANGUAGE_CODE = 'zh-Hans'   #原配置信息为'en-us'

TIME_ZONE = 'Asia/Shanghai'#原配置信息为'UTC'

创建管理员
1.我们需要在User模型中设置 REQUIRED_FIELDS
在 users.modle.py中设置配置信息

#创建超级管理员的需要必须输入的字段
REQUIRED_FIELDS = ['username','email']

在这里插入图片描述

2.在终端创建超级管理员

创建管理员的命令 :

  python manage.py createsuperuser

在这里插入图片描述
然后重新登陆进入 站点服务
注册模型类
在应用的admin.py文件中注册模型类

需要导入模型模块 :from home.models import ArticleCategory
在这里插入图片描述

模型注册完之后我们即可在站点管理进行对分类操作
在这里插入图片描述
模型类展示我们输入的内容是因为我们在模型中实现了__str_方法_
home.model.py

class ArticleCategory(models.Model):
    """
    文章分类
    """
    # 栏目标题
    title = models.CharField(max_length=100, blank=True)
    # 创建时间
    created = models.DateTimeField(default=timezone.now)

    def __str__(self):
        return self.title

写博客页面展示分类

  1. 查询数据并展示
    1.查询分类文章数据并通过context传递给HTML
from home.models import ArticleCategory
class WriteBlogView(LoginRequiredMixin,View):

    def get(self,request):
        # 获取博客分类信息
        categories = ArticleCategory.objects.all()

        context = {
            'categories': categories
        }
        return render(request,'write_blog.html',context=context)

2.在write_blog.html文件中使用模板语言展示数据

<!-- 文章栏目 -->
<div class="form-group">
    <label for="column">栏目</label>
    <select class="form-control col-3" id="category" name="category">
            {% for category in categories %}
                <option value="{{ category.id }}">{{ category.title }}</option>
            {% endfor %}
    </select>
</div>

在这里插入图片描述

文章模型

在home子应用的models.py模型中定义文章模型

from users.models import User 
class Article(models.Model):
    """
    文章
    """
    # 定义文章作者。 author 通过 models.ForeignKey 外键与内建的 User 模型关联在一起
    # 参数 on_delete 用于指定数据删除的方式,避免两个关联表的数据不一致。
    author = models.ForeignKey(User, on_delete=models.CASCADE)
    # 文章标题图
    avatar = models.ImageField(upload_to='article/%Y%m%d/', blank=True)
    # 文章栏目的 “一对多” 外键
    category = models.ForeignKey(
        ArticleCategory,
        null=True,
        blank=True,
        on_delete=models.CASCADE,
        related_name='article'
    )
    # 文章标签
    tags = models.CharField(max_length=20,blank=True)
    # 文章标题。
    title = models.CharField(max_length=100,null=False,blank=False)
    # 概要
    sumary = models.CharField(max_length=200,null=False,blank=False)
    # 文章正文。
    content = models.TextField()
    # 浏览量
    total_views = models.PositiveIntegerField(default=0)
    # 文章评论数
    comments_count = models.PositiveIntegerField(default=0)
    # 文章创建时间。
    # 参数 default=timezone.now 指定其在创建数据时将默认写入当前的时间
    created = models.DateTimeField(default=timezone.now)
    # 文章更新时间。
    # 参数 auto_now=True 指定每次数据更新时自动写入当前时间
    updated = models.DateTimeField(auto_now=True)

    # 内部类 class Meta 用于给 model 定义元数据
    class Meta:
        # ordering 指定模型返回的数据的排列顺序
        # '-created' 表明数据应该以倒序排列
        ordering = ('-created',)
        db_table='tb_article'
        verbose_name='文章管理'
        verbose_name_plural=verbose_name
    # 函数 __str__ 定义当调用对象的 str() 方法时的返回值内容
    # 它最常见的就是在Django管理后台中做为对象的显示值。因此应该总是为 __str__ 返回一个友好易读的字符串
    def __str__(self):
        # 将文章标题返回
        return self.title

迁移模型类
1.创建迁移文件

python manage.py makemigrations

2.执行迁移文件

python manage.py migrate

博客保存

博客保存接口设计
1.请求方式

选项方案
请求方法POST
请求地址/writeblog/

2.请求参数:表单

参数名类型是否必传说明
title string标题
avatar file标题图
categorystring栏目分类
tagsstring标签
sumarystring文章摘要
contentstring文章内容

3.响应结果:HTML

字段说明
提交失败响应错误提示
提交成功跳转到详情页面
  1. 用户中心修改接口实现
from home.models import ArticleCategory,Article
class WriteBlogView(LoginRequiredMixin,View):

    def post(self,request):
        #接收数据
        avatar=request.FILES.get('avatar')
        title=request.POST.get('title')
        category_id=request.POST.get('category')
        tags=request.POST.get('tags')
        sumary=request.POST.get('sumary')
        content=request.POST.get('content')
        user=request.user

        #验证数据是否齐全
        if not all([avatar,title,category_id,sumary,content]):
            return HttpResponseBadRequest('参数不全')

        #判断文章分类id数据是否正确
        try:
            article_category=ArticleCategory.objects.get(id=category_id)
        except ArticleCategory.DoesNotExist:
            return HttpResponseBadRequest('没有此分类信息')

        #保存到数据库
        try:
            article=Article.objects.create(
                author=user,
                avatar=avatar,
                category=article_category,
                tags=tags,
                title=title,
                sumary=sumary,
                content=content
            )
        except Exception as e:
            logger.error(e)
            return HttpResponseBadRequest('发布失败,请稍后再试')

        #返回响应,跳转到文章详情页面
        #暂时先跳转到首页
        return redirect(reverse('home:index'))

在这里插入图片描述

首页分类数据展示

2.定义首页视图:IndexView—查询分类数据并展示
2.1.请求方式

选项方案
请求方法GET
请求地址/?cat_id=xxx&page_num=xxx&page_size=xxx

2.2.请求参数

参数名类型是否必传说明
cat_idstring分类id
page_numstring文章分页页码
page_sizestring文章每页条目数

2.3.响应结果:HTML

字段说明
失败响应错误提示
成功展示数据

1.查询分类文章数据并通过context传递给HTML
home.views.py


from home.models import ArticleCategory
from django.http import HttpResponseNotFound

class IndexView(View):
    """首页广告"""

    def get(self, request):
        """提供首页广告界面"""
        #?cat_id=xxx&page_num=xxx&page_size=xxx
        cat_id=request.GET.get('cat_id',1)

        #判断分类id
        try:
            category = ArticleCategory.objects.get(id=cat_id)
        except ArticleCategory.DoesNotExist:
            return HttpResponseNotFound('没有此分类')

        # 获取博客分类信息
        categories = ArticleCategory.objects.all()

        context = {
            'categories':categories,
            'category':category
        }

        return render(request, 'index.html',context=context)

2.在index.html文件中使用模板语言展示分类数据

<ul class="nav navbar-nav">
    {% for cat in categories %}
        {% if cat.id == category.id %}
            <li class="nav-item active">
                <a class="nav-link mr-2" href="/?cat_id={{ cat.id }}">{{ cat.title }}</a>
            </li>
        {% else %}
            <li class="nav-item">
                <a class="nav-link mr-2" href="/?cat_id={{ cat.id }}">{{ cat.title }}</a>
            </li>
        {% endif %}
    {% endfor %}
</ul>

查询分页数据并展示

from home.models import ArticleCategory,Article
from django.http import HttpResponseNotFound
from django.core.paginator import Paginator,EmptyPage

class IndexView(View):
    """首页广告"""

    def get(self, request):
        """提供首页广告界面"""
        #?cat_id=xxx&page_num=xxx&page_size=xxx
        cat_id=request.GET.get('cat_id',1)
        page_num = request.GET.get('page_num', 1)
        page_size = request.GET.get('page_size', 10)
        #判断分类id
        try:
            category = ArticleCategory.objects.get(id=cat_id)
        except ArticleCategory.DoesNotExist:
            return HttpResponseNotFound('没有此分类')

        # 获取博客分类信息
        categories = ArticleCategory.objects.all()

        #分页数据
        articles = Article.objects.filter(
            category=category
        )

        # 创建分页器:每页N条记录
        paginator = Paginator(articles, page_size)
        # 获取每页商品数据
        try:
            page_articles = paginator.page(page_num)
        except EmptyPage:
            # 如果没有分页数据,默认给用户404
            return HttpResponseNotFound('empty page')
        # 获取列表页总页数
        total_page = paginator.num_pages

        context = {
            'categories':categories,
            'category':category,
            'articles': page_articles,
            'page_size': page_size,
            'total_page': total_page,
            'page_num': page_num,
        }

        return render(request, 'index.html',context=context)

2.在index.html文件中使用模板语言展示分类数据

<div class="container">
    <!-- 列表循环 -->
     {% for article in articles %}
        <div class="row mt-2">
            <!-- 文章内容 -->
            <!-- 标题图 -->
            <div class="col-3">
                <img src="{{ article.avatar.url }}" alt="avatar" style="max-width:100%; border-radius: 20px">
            </div>
            <div class="col">
                <!-- 栏目 -->
                <a  role="button" class="btn btn-sm mb-2 btn-warning">{{ article.category.title }}</a>
            <!-- 标签 -->
                <span>
                        <a class="badge badge-secondary">{{ article.tags }}</a>
                </span>
                <!-- 标题 -->
                <h4>
                    <b><a href="./detail.html" style="color: black;">{{ article.title }}</a></b>
                </h4>
                <!-- 摘要 -->
                <div>
                    <p style="color: gray;">
                        {{ article.sumary }}
                    </p>
                </div>
                <!-- 注脚 -->
                <p>
                    <!-- 查看、评论、时间 -->
                    <span><i class="fas fa-eye" style="color: lightskyblue;"></i>{{ article.total_views }}&nbsp;&nbsp;&nbsp;</span>
                    <span><i class="fas fa-comments" style="color: yellowgreen;"></i>{{ article.comments_count }}&nbsp;&nbsp;&nbsp;</span>
                    <span><i class="fas fa-clock" style="color: pink;"></i>{{ article.created | date }}</span>
                </p>
            </div>
            <hr style="width: 100%;"/>
    </div>
    {% endfor %}
    <!-- 页码导航 -->
    <div class="pagenation" style="text-align: center">
        <div id="pagination" class="page"></div>
    </div>
</div>

3.修改底部js分页代码

<script type="text/javascript">
    $(function () {
        $('#pagination').pagination({
            currentPage: {{ page_num }},
            totalPage: {{ total_page }},
            callback:function (current) {

                location.href = '/?cat_id={{ category.id }}&page_size={{ page_size }}&page_num='+current;
            }
        })
    });
</script>

博客详情

详情页面展示

  1. 页面展示
    1.在home.views.py文件中定义视图
from django.views import View

class DetailView(View):

    def get(self,request):


        return render(request,'detail.html')

2.在home.urls.py文件中定义路由

from users.views import DetailView
urlpatterns = [
    # 参数1:路由
    # 参数2:视图函数
    # 参数3:路由名,方便通过reverse来获取路由
    path('detail/', DetailView.as_view(),name='detail'),
]

3.修改detail.html中的资源加载方式

    <!-- Header部分 -->
       {% load staticfiles %}
    <!-- 引入bootstrap的css文件 -->
    <link rel="stylesheet" href="{% static 'bootstrap/css/bootstrap.min.css' %}">
    <!--详情页面导入-->
    <script src="{% static 'ckeditor/ckeditor/plugins/prism/lib/prism/prism_patched.min.js' %}"></script>
    <link rel="stylesheet" href="{% static 'prism/prism.css' %}">
    <!--导入css-->
    <link rel="stylesheet" href="{% static 'common/common.css' %}">
    <link rel="stylesheet" href="{% static 'common/jquery.pagination.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>
    <script type="text/javascript" src="{% static 'js/jquery-1.12.4.min.js' %}"></script>
    ...
    <!-- Footer部分 -->
    <!--ckeditor-->
    <script type="text/javascript" src="{% static 'ckeditor/ckeditor-init.js' %}" data-ckeditor-basepath="{% static 'ckeditor/ckeditor/' %}" id="ckeditor-init-script"></script>
    <script type="text/javascript" src="{% static 'ckeditor/ckeditor/ckeditor.js' %}"></script>
    <!-- 引入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/detail.js' %}"></script>
    <script type="text/javascript" src="{% static 'js/jquery.pagination.min.js' %}"></script>
    ...
    <!-- 页面跳转部分 -->
    <a class="dropdown-item" href="{% url 'users:writeblog' %}">写文章</a>
    <a class="dropdown-item" href='{% url 'users:center'%}'>个人信息</a>
    <a class="dropdown-item" href='{% url 'users:center' %}'>退出登录</a>
  1. 查询分类数据并展示
    1.查询文章数据并通过context传递给HTML
class DetailView(View):

    def get(self,request):
        # detail/?id=xxx&page_num=xxx&page_size=xxx
        #获取文档id
        id=request.GET.get('id')

        # 获取博客分类信息
        categories = ArticleCategory.objects.all()

        try:
            article=Article.objects.get(id=id)
        except Article.DoesNotExist:
            return render(request,'404.html')

        context = {
            'categories':categories,
            'category':article.category,
            'article':article,
        }

        return render(request,'detail.html',context=context)

2.在detail.html文件中使用模板语言展示文章数据

#分类数据展示
 <div>
    <ul class="nav navbar-nav">
        {% for cat in categories %}
            {% if cat.id == category.id %}
                <li class="nav-item active">
                    <a class="nav-link mr-2" href="/?cat_id={{ cat.id }}">{{ cat.title }}</a>
                </li>
            {% else %}
                <li class="nav-item">
                    <a class="nav-link mr-2" href="/?cat_id={{ cat.id }}">{{ cat.title }}</a>
                </li>
            {% endif %}
        {% endfor %}
    </ul>
</div>

#详情数据展示
 <!-- 标题及作者 -->
<h1 class="mt-4 mb-4">{{ article.title }}</h1>
<div class="alert alert-success"><div>作者:<span>{{ article.author.username }}</span></div><div>浏览:{{ article.total_views }}</div></div>
<!-- 文章正文 -->
<div class="col-12" style="word-break: break-all;word-wrap: break-word;">
    {{ article.content|safe }}
</div>
<br>

在这里插入图片描述

  1. 修改首页跳转到详情页面的链接

<!-- 标题 -->
<h4>
    <b><a href="{% url 'home:detail' %}?id={{ article.id }}" style="color: black;">{{ article.title }}</a></b>
</h4>

推荐文章数据展示

  1. 添加文章浏览量数据
    1.每次请求文章详情时给浏览量+1
try:
    article=Article.objects.get(id=id)
except Article.DoesNotExist:
    return render(request,'404.html')
else:
    article.total_views+=1
    article.save()
  1. 查询推荐文章并展示
    1.查询推荐文章数据并通过context传递给HTML
class DetailView(View):

    def get(self,request):
        # detail/?id=xxx&page_num=xxx&page_size=xxx
        #获取文档id
        id=request.GET.get('id')

        # 获取博客分类信息
        categories = ArticleCategory.objects.all()

        try:
            article=Article.objects.get(id=id)
        except Article.DoesNotExist:
            return render(request,'404.html')
        else:
            article.total_views+=1
            article.save()

        # 获取热点数据
        hot_articles = Article.objects.order_by('-total_views')[:9]

        context = {
            'categories':categories,
            'category':article.category,
            'article':article,
            'hot_articles':hot_articles
        }
        return render(request,'detail.html',context=context)

2.在detail.html文件中使用模板语言展示推荐数据

<div class="sidebar__inner">
    <h4><strong>推荐</strong></h4>
    <hr>
    {% for hot_article in hot_articles %}
        <a href="{% url 'home:detail' %}?id={{ hot_article.id }}" style="color: black">{{ hot_article.title }}</a><br>
    {% endfor %}
    </div>
</div>

评论模型

  1. 定义模型类
    在home子应用的models.py模型中定义评论模型
class Comment(models.Model):
    #评论内容
    content=models.TextField()
    #评论的文章
    article=models.ForeignKey(Article,
                              on_delete=models.SET_NULL,
                              null=True)
    #发表评论的用户
    user=models.ForeignKey('users.User',
                           on_delete=models.SET_NULL,
                           null=True)
    #评论发布时间
    created=models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.article.title

    class Meta:
        db_table='tb_comment'
        verbose_name = '评论管理'
        verbose_name_plural = verbose_name
  1. 迁移模型类
    1.创建迁移文件
python manage.py makemigrations

2.执行迁移文件

python manage.py migrate

发表评论

  1. 发表评论接口设计
    1.请求方式
选项方案
请求方法POST
请求地址/detail/

2.请求参数:表单

参数名类型是否必传说明
user_idstring发表评论的用户id
article_idstring评论的文字id
contentstring评论内容

3.响应结果:HTML

字段说明
提交失败响应错误提示
提交成功刷新页面展示
  1. 发表评论接口实现
    1.发表评论实现
from home.models import Comment,Article
class DetailView(View):

    def post(self,request):
        #获取用户信息
        user=request.user

        #判断用户是否登录
        if user and user.is_authenticated:
            #接收数据
            id=request.POST.get('id')
            content=request.POST.get('content')

            #判断文章是否存在
            try:
                article = Article.objects.get(id=id)
            except Article.DoesNotExist:
                return HttpResponseNotFound('没有此文章')

            #保存到数据
            Comment.objects.create(
                content=content,
                article=article,
                user=user
            )
            #修改文章评论数量
            article.comments_count+=1
            article.save()
            #拼接跳转路由
            path=reverse('home:detail')+'?id={}'.format(article.id)
            return redirect(path)
        else:
            #没有登录则跳转到登录页面
            return redirect(reverse('users:login'))

2.detail.html修改

 <form method="POST">
    {% csrf_token %}
    <input type="hidden" name="id" value="{{ article.id }}">
    <div class="form-group"><label for="body"><strong>我也要发言:</strong></label>
        <div>
            <div class="django-ckeditor-widget" data-field-id="id_body" style="display: inline-block;">
                <textarea cols="40" id="id_body" name="content" rows="10" required data-processed="0" :data-config="data_config" data-external-plugin-resources="[]" data-id="id_body" data-type="ckeditortype">

                </textarea>
            </div>
        </div>
    </div>
    <!-- 提交按钮 -->
    <button type="submit" class="btn btn-primary ">发送</button>
</form>

详情评论数据展示

  1. 查询评论数据并展示
    1.查询评论数据并通过context传递给HTML
    home.views.py
from home.models import Comment
from django.shortcuts import redirect,reverse
class DetailView(View):

    def get(self,request):
        # detail/?id=xxx&page_num=xxx&page_size=xxx
        #获取文档id
        id=request.GET.get('id')
        page_num=request.GET.get('page_num',1)
        page_size=request.GET.get('page_size',5)
        # 获取博客分类信息
        categories = ArticleCategory.objects.all()

        try:
            article=Article.objects.get(id=id)
        except Article.DoesNotExist:
            return render(request,'404.html')
        else:
            article.total_views+=1
            article.save()

        # 获取热点数据
        hot_articles = Article.objects.order_by('-total_views')[:9]

        # 获取当前文章的评论数据
        comments = Comment.objects.filter(
            article=article
        ).order_by('-created')
        #获取评论总数
        total_count = comments.count()

        # 创建分页器:每页N条记录
        paginator = Paginator(comments, page_size)
        # 获取每页商品数据
        try:
            page_comments = paginator.page(page_num)
        except EmptyPage:
            # 如果page_num不正确,默认给用户404
            return HttpResponseNotFound('empty page')
        # 获取列表页总页数
        total_page = paginator.num_pages

        context = {
            'categories':categories,
            'category':article.category,
            'article':article,
            'hot_articles':hot_articles,
            'total_count': total_count,
            'comments': page_comments,
            'page_size': page_size,
            'total_page': total_page,
            'page_num': page_num,
        }

        return render(request,'detail.html',context=context)

在这里插入图片描述

2.在index.html文件中使用模板语言展示分类数据

<!-- 显示评论 -->
<h4>共有{{ total_count }}条评论</h4>
<div class="row">
     {% for comment in comments %}
        <div class="col-12" >
            <hr><p><strong style="color: pink"></strong></p>
            <div>
                <div><span><strong>{{ comment.user.username }}</strong></span>&nbsp;<span style="color: gray">{{ comment.created | date:'Y:m:d H:i:s' }}</span></div>
                <br>
                <p>{{ comment.content|safe }}</p>
            </div>
        </div>
    {% endfor %}
    <div class="pagenation" style="text-align: center">
        <div id="pagination" class="page"></div>
    </div>
</div>

3.修改底部js分页代码

<script type="text/javascript">
    $(function () {
        $('#pagination').pagination({
           currentPage: {{ page_num }},
            totalPage: {{ total_page }},
            callback:function (current) {
                location.href = '/detail/?id={{ article.id }}&page_size={{ page_size }}&page_num='+current;
            }
        })
    });
</script>

现在就是完成基本使用功能一个完整的**登录 注册 退出 发表文章 评论文章 **
后期待完善

;原文链接:https://blog.csdn.net/qq_41961239/article/details/115736805
本站部分内容转载于网络,版权归原作者所有,转载之目的在于传播更多优秀技术内容,如有侵权请联系QQ/微信:153890879删除,谢谢!
上一篇:独立对honor荣耀来说有哪些好处? 下一篇:没有了

推荐图文


随机推荐