教程全知识点简介:数据模型 模板管理 模板管理业务流程 模板制作 GridFS研究 模板存储 静态化测试 页面预览 4 添加“页面预览”链接 页面发布 需求分析 技术方案 测试 课程管理 环境搭建 我的课程 数据字典 新增课程页面完善 新增课程服务端 课程信息修改 课程管理页面说明 服务端 前端 课程计划 页面原型 课程图片 上传图片开发 项目概述 项目背景 项目的功能构架 项目的技术架构 CMS 什么是CMS CMS需求分析与工程搭建 静态门户工程搭建 图片查询 前端开发 课程预览 课程预览技术方案 课程详情页面静态化 课程数据模型查询接口 课程信息模板设计 课程预览功能开发 CMS页面预览测试 CMS添加页面接口 课程发布 搜索服务 课程搜索需求分析 课程索引 准备课程索引信息 搭建ES环境 Logstash创建索引 搜索前端技术需求 了解SEO 服务端渲染和客户端渲染 Nuxt.js入门 创建Nuxt工程 页面布局 路由 搜索页面 分页查询 按分类搜索 集成测试 在线学习与媒资管理 在线学习需求分析 需求描述 视频点播解决方案 视频编码 生成m3u8/ts文件 播放器 后端工程搭建 导入CMS数据库 MongoEngine入门 在Django中使用MongoDB 定义文档 开发环境 上传文件 断点续传解决方案 文件分块与合并 前端页面 媒资服务端编写 媒体视频处理队列 媒体视频路由key 媒体处理交换机 加载可用的任务 保存视频信息 学习页面查询课程计划 Api接口 Logstash扫描课程计划媒资 搜索服务查询课程媒资接口 ESClient类 JWT 用户登录前端 用户注册后端 订单与选课 分布式事务 问题描述 CAP理论 解决方案 自动添加选课方案 增加数据 修改数据 删除数据 查询数据 Q对象 其他 查询集 QuerySet 页面管理后端开发 Celery Beat定时任务 订单服务定时发送消息 查询前N条任务 接口说明 接口开发规范 封装异常处理 封装JSON返回值 站点列表和模板列表接口 新增页面 页面查询接口定义 自定义条件查询 3 测试接口 页面详情 修改页面 删除页面 CMS前端工程创建 导入系统管理前端工程 单页面应用介绍 CMS前端页面查询开发 创建页面 Table组件测试 进入页面立即查询 前后端请求响应流程小结 新增页面前端开发 添加页面测试 页面处理流程 页面静态化及预览发布 页面静态化需求

<!-- start:bj1 -->

<h3>📚📚  整个项目完整代码和资料传送门  📚📚</h3>

👉🍅 https://gitee.com/yinuo112/Backend/blob/master/Python/嘿马云课堂web完整实战项目/note.md

<!-- end:bj1 -->

新增页面

学习目标

  • 了解新增页面的需求
  • 完成新增页面的接口定义

1 需求分析

在梳理完用户需求后就要去定义前后端的接口,接口定义后前端和后端就可以依据接口去开发功能了。

本次定义的新增页面接口,本接口接收相关页面参数,创建页面后返回新建的页面数据。

2 新增页面接口定义

CmsPage数据模型在前面已经定义过了,下面直接看一下新增页面的接口:

添加CMS页面

请求方式:POST /cms/page/add

请求参数

参数 类型 是否必须 说明
siteId ObjectId 站点ID
templateId ObjectId 模板ID
pageName str 页面名称
pageAlias str 页面别名
pageType str 页面类型
pageWebPath str 页面访问路径
pagePhysicalPath str 页面物理路径

返回数据: JSON

{"pageId": "5d7752bf29bff90d1028ee4e","pageCreateTime": "2019-09-10T15:37:35.204168","dataUrl": "string","htmlFileId": "string","pageAlias": "string","pageHtml": "string","pageName": "测试页面","pageParameter": "string","pagePhysicalPath": "string","pageStatus": "string","pageType": "string","pageWebPath": "string","siteId": "5a751fab6abb5044e0d19ea1","templateId": "5a925be7b00ffc4b3c1578b5"
}
参数 类型 是否必须 说明
pageId ObjectId 页面ID
siteId ObjectId 站点ID
templateId ObjectId 模板ID
pageName str 页面名称
pageAlias str 页面别名
pageType str 页面类型
pageWebPath str 页面访问路径
pagePhysicalPath str 页面物理路径
pageStatus str 页面状态
pageCreateTime datetime 页面创建时间
dataUrl str 数据url

3 定义序列化器

apps/cms/serializers.py

from cms.models import CmsSite, CmsTemplate, CmsPage...
class CmsPageSerializer(DocumentSerializer):pageId = CharField(source='id', read_only=True)class Meta:model = CmsPagefields = ('pageId', 'siteId', 'pageName', 'pageAlias', 'pageWebPath', 'pagePhysicalPath', 'pageType', 'pageStatus', 'pageCreateTime', 'templateId', 'dataUrl')read_only_fields = ('pageCreateTime', )

4 定义视图集

4.1 页面唯一索引

在cms_page集中上创建页面名称、站点Id、页面webpath为唯一索引

> db.cms_page.ensureIndex({pageName: 1, siteId: 1, pageWebPath: 1}, {unique: true});

4.2 定义视图集

apps/cms/views.py

from cms.models import CmsSite, CmsTemplate, CmsPage
from cms.serializers import CmsSiteSerializer, CmsTemplateSerializer, CmsPageSerializer...
class CmsPageViewSet(ModelViewSet):queryset = CmsPage.objectsserializer_class = CmsPageSerializerpagination_class = paginations.PathPagePaginationdef create(self, request, *args, **kwargs):queryset = self.get_queryset()filter_set = queryset.filter(pageName=request.data.get('pageName'), siteId=request.data.get('siteId'), pageWebPath=request.data.get('pageWebPath'))if filter_set:exceptions.cast_exception(response_code.CMS_ADDPAGE_EXISTS)return super().create(request, *args, **kwargs)

5 定义路由

apps/cms/urls.py

urlpatterns = [...url(r'^cms/page/add$', CmsPageViewSet.as_view({'post': 'create'})),
]

6 测试接口

test/cms.py

import requests
base_url = 'http://localhost:8000'def add_cms_page():url = base_url + '/cms/page/add'resp = requests.post(url, json={"dataUrl": "string","pageAlias": "string","pageName": "测试页面","pagePhysicalPath": "string","pageStatus": "string","pageType": "string","pageWebPath": "string","siteId": "5a751fab6abb5044e0d19ea1","templateId": "5a925be7b00ffc4b3c1578b5"})print(resp.json())if __name__ == '__main__':add_cms_page()

运行cms.py,打印出新创建的cms_page数据:

{'success': True, 'code': 10000, 'message': '操作成功!', 'data': {'pageId': '5d77571229bff90d1028ee50', 'siteId': '5a751fab6abb5044e0d19ea1', 'pageName': '测试页面', 'pageAlias': 'string', 'pageWebPath': 'string', 'pagePhysicalPath': 'string', 'pageType': 'string', 'pageStatus': 'string', 'pageCreateTime': '2019-09-10T15:56:02.918263', 'templateId': '5a925be7b00ffc4b3c1578b5', 'dataUrl': 'string'}}

再次运行,打印出如下信息:

{'success': False, 'code': 24002, 'message': '页面已存在!', 'data': {}}

页面查询接口定义

需求分析

学习目标

  • 了解查询页面列表的需求

1 需求分析

本次定义页面查询接口,本接口供前端请求查询页面列表,支持分页及自定义条件查询方式。

具体需求如下:

1、分页查询CmsPage 集合下的数据

2、根据站点Id、模板Id、页面别名查询页面信息

3、接口基于Http Get请求,响应Json数据

分页查询

学习目标

  • 掌握页面列表的定义方法
  • 掌握自定义分页器的方法

1 定义接口

获取CMS页面列表

请求方式:GET /cms/page/list/<page>/<size>

Requests 文档

请求参数:query_params

参数 类型 是否必须 说明
siteId ObjectId 站点ID
templateId ObjectId 模板ID
pageAlias str 页面别名
pageName str 页面名称
pageType str 页面类型

返回数据: JSON

{"total": 12,"page": 1,"pages": 2,"list": [{"pageId": "5d7752bf29bff90d1028ee4e","pageCreateTime": "2019-09-10T15:37:35.204168","dataUrl": "string","htmlFileId": "string","pageAlias": "string","pageHtml": "string","pageName": "测试页面","pageParameter": "string","pagePhysicalPath": "string","pageStatus": "string","pageType": "string","pageWebPath": "string","siteId": "5a751fab6abb5044e0d19ea1","templateId": "5a925be7b00ffc4b3c1578b5"}]
}

Matplotlib 文档

pydantic 文档

参数 类型 是否必须 说明
total int 数据总数
page int 当前页面
pages int 总页数
list 数组 CMS页面数组

list - CMS页面数组

参数 类型 是否必须 说明
pageId ObjectId 页面ID
siteId ObjectId 站点ID
templateId ObjectId 模板ID
pageName str 页面名称
pageAlias str 页面别名
pageType str 页面类型
pageWebPath str 页面访问路径
pagePhysicalPath str 页面物理路径
pageStatus str 页面状态
pageCreateTime datetime 页面创建时间
dataUrl str 数据url

2 定义序列化器

序列化器未做改变。

3 定义pagination

Django RESTframework默认的pagination不能满足我们的需求,需要定义我们自己的pagination:

1、在url路径中读取分页参数。

2、按total、page、pages、list的格式返回的分页数据。

common/paginations.py

from rest_framework.pagination import PageNumberPagination
from rest_framework.response import Responseclass PathPagePagination(PageNumberPagination):"""定义分页器"""page = 10  # 在没有指定每页返回数量的情况下,默认返回10条数据page_query_param = 'page'page_size_query_param = 'size'  # 指定控制每页显示数量的参数名称max_page_size = 50def paginate_queryset(self, queryset, request, view=None):# 将路由中的分页参数放入query_params中query_params = request.query_params.copy()query_params.update(**request.parser_context['kwargs'])request._request.GET = query_paramsreturn super().paginate_queryset(queryset, request, view)# 改写分页结果返回方法def get_paginated_response(self, data):return Response({'total': self.page.paginator.count,'list': data,'page': self.page.number,  # 获取当前页码'pages': self.page.paginator.num_pages,})

4 定义视图集

class CmsPageViewSet(ModelViewSet):queryset = CmsPage.objectsserializer_class = CmsPageSerializerpagination_class = paginations.PathPagePagination

5 定义路由

Playwright Python

apps/cms/urls.py

urlpatterns = [...url(r'^cms/page/list/(?P<page>\d+)/(?P<size>\d+)$', CmsPageViewSet.as_view({'get': 'list'})),
]

6 测试接口

rich 文档

访问 http://localhost:8000/cms/page/list/1/10 ,结果如下:

自定义条件查询

学习目标

  • 了解自定义条件查询的需求
  • 掌握自定义条件查询的方法

1 需求分析

在页面输入查询条件,查询符合条件的页面信息。

查询条件如下:

站点Id:精确匹配

模板Id:精确匹配

页面类型:精确匹配,页面类型包括:静态和动态,在数据库中静态用“0”表示,动态用“1”表示。

页面别名:模糊匹配

页面名称:模糊匹配

2 修改视图集

apps/cms/views.py

class CmsPageViewSet(ModelViewSet):...def filter_queryset(self, queryset):filters = {}if self.request.query_params.get('siteId'):filters.update({'siteId': self.request.query_params.get('siteId')})if self.request.query_params.get('templateId'):filters.update({'templateId': self.request.query_params.get('templateId')})if self.request.query_params.get('pageType'):filters.update({'pageType': self.request.query_params.get('pageType')})if self.request.query_params.get('pageAlias'):filters.update({'pageAlias__contains': self.request.query_params.get('pageAlias')})if self.request.query_params.get('pageName'):filters.update({'pageName__contains': self.request.query_params.get('pageName')})if filters:queryset = queryset.filter(**filters)return queryset

3 测试接口

3.1 添加测试数据

在mongo客户端插入测试数据

db.cms_page.insert([{"_id" : ObjectId("5d7764e96f163a20e7ca031f"), "pageAlias": "测试轮播图", "templateId": ObjectId("5a962bf8b00ffc514038fafa"), "pageName": "测试页面1", "pageType": "1", "pageWebPath": "string", "pagePhysicalPath": "string", "pageStatus": "string", "dataUrl": "string", "siteId": ObjectId("5a751fab6abb5044e0d19ea1")},{"_id" : ObjectId("5d7764e96f163a20e7ca0320"), "pageAlias": "测试详情页", "templateId": ObjectId("5a962b52b00ffc514038faf7"), "pageName": "测试页面2", "pageType": "0", "pageWebPath": "string", "pagePhysicalPath": "string", "pageStatus": "string", "dataUrl": "string", "siteId": ObjectId("5a751fab6abb5044e0d19ea1")},{"_id" : ObjectId("5d7764e96f163a20e7ca0321"), "pageAlias": "轮播图", "templateId": ObjectId("5a962bf8b00ffc514038fafa"), "pageName": "页面3", "pageType": "1", "pageWebPath": "string1", "pagePhysicalPath": "string", "pageStatus": "string", "dataUrl": "string", "siteId": ObjectId("5a751fab6abb5044e0d19ea1")},{"_id" : ObjectId("5d7764e96f163a20e7ca0322"), "pageAlias": "详情页", "templateId": ObjectId("5a962b52b00ffc514038faf7"), "pageName": "页面4", "pageType": "0", "pageWebPath": "string1", "pagePhysicalPath": "string", "pageStatus": "string", "dataUrl": "string", "siteId": ObjectId("5a751fab6abb5044e0d19ea1")},
])

Click 文档

3.2 测试接口

访问 [http://localhost:8000/cms/page/lis