DRF的分页组件,过滤组件

1|0一、DRF中的分页组件

  • DRF自带的分页组件帮我们写好了分页功能,包括各种小问题的优化,已经能满足实际开发的需求。
  • 这里介绍了DRF的两种分页组件
    • 基础分页组件
    • 偏移分页组件

1|11. 分页组件的使用

  • 使用方法:类似DRF的序列化的使用方式
1. 新建一个pagination.py文件,自定义分页类 # pagination.py文件中: from rest_framework import pagination # 自定义基础分页类 class PageNumberPagination(pagination.PageNumberPagination): # 默认一页显示的条数 page_size = 2 # 查询页面的关键字,就是在url中参数的key page_query_param = 'page' # 用户自定义一页显示条数的关键字,就是在url中参数的key page_size_query_param = 'page_size' # 用户最大可自定义一页显示的条数 max_page_size = 2 # 自定义偏移分页类 class LimitOffsetPagination(pagination.LimitOffsetPagination): # 默认一页显示的条数 default_limit = 2 # 用户自定义一页显示的条数 limit_query_param = 'limit' # 用户自定义偏移的条数 offset_query_param = 'offset' # 用户最大可自定义一页显示的条数 max_limit = 2 2. 在views.py文件中配置任意一个分页类 *******群查接口的视图类中:********* from . import paginations class FreeCourseListViewSet(ListModelMixin, GenericViewSet): queryset = models.Course.objects.filter(is_delete=False, is_show=True).all() serializer_class = serializers.CourseModelSerializer # 配置某一个分页类即可,用法是一样的,这里配置的是基础分页类 pagination_class = paginations.PageNumberPagination # 注:接口分页前后,response的格式不一样 # 分页前:数据是response.data # 分页后:数据是response.data.results 3. 访问接口的url形式 1)基础分页的url格式,不加拼接参数,则默认不分页 127.0.0.1:8000/courses/?page=数字&page_size=数字 2)偏移分页的url格式,不加拼接参数,则默认不分页 127.0.0.1:8000/courses/?limit=数字&offset=数字

2|0二、DRF的过滤组件

  • 过滤组件包括 搜索和排序

2|11. 搜索过滤组件的使用

  • 搜索组件就是提供关键字到后端,将该关键字与绑定的模型类的字段进行模糊匹配,将匹配到的全部筛选出来
1. views.py文件中配置搜索组件和搜索字段 *******群查接口的视图类中:********* from rest_framework.filters import SearchFilter class FreeCourseListViewSet(ListModelMixin, GenericViewSet): queryset = models.Course.objects.filter(is_delete=False, is_show=True).all() serializer_class = serializers.CourseModelSerializer # 配置搜索组件 filter_backends = [SearchFilter] # 配置参与搜索字段(只能是模型类中的字段,不包括后来自定义的字段) search_fields = ['name', 'brief'] # 规则: # 参与全文搜索的字段为name和brief(两个字段必须都是数据库表字段) # 接口:?search=搜索关键字,会全文匹配name和brief两个字段 2. 访问接口的url形式 127.0.0.1:8000/courses/?search=关键字 # 该关键字会匹配 search_fields 中配置的所有字段所对应的数据

2|22. 排序过滤组件的使用

  • 就是对查询结果按照模型类的字段进行升序或降序排序
1. views.py文件中配置排序组件和排序字段 *******群查接口的视图类中:********* from rest_framework.filters import OrderingFilter class FreeCourseListViewSet(ListModelMixin, GenericViewSet): queryset = models.Course.objects.filter(is_delete=False, is_show=True).all() serializer_class = serializers.CourseModelSerializer # 配置排序组件 filter_backends = [OrderingFilter] # 配置参与排序字段 ordering_fields = ['price', 'id', 'students'] # 规则: # ?ordering=price 按价格升序 # ?ordering=-price 按价格降序 # ?ordering=id 按主键升序 # ?ordering=-price,id 按价格降序,价格相同时按主键升序 2. 访问接口的url的格式 127.0.0.1:8000/courses/?ordering=配置的排序字段 # 排序字段可加负号,表示降序

2|33. 自定义过滤类

  • 模仿DRF的过滤类,实现自定义过滤类
1. 新建filters.py文件,用来自定义过滤类 # 本自定义过滤器功能:实现filter_queryset可以接收request, queryset, view返回过滤处理后的queryset即可 class MyFilter: def filter_queryset(self, request, queryset, view): # 过滤条件是死的,?count=数字,也可以写高级一点,从view中去反射配置信息,或者将配置信息放在settings中 count = request.query_params.get('count', None) if not count: return queryset # 没过滤 try: count = int(count) return queryset[:count] # 过滤 except: return queryset # 没过滤 2. 在views.py文件中导入使用自定义的过滤类 *******群查接口的视图类中:********* from .filters import MyFilter class FreeCourseListViewSet(ListModelMixin, GenericViewSet): queryset = models.Course.objects.filter(is_delete=False, is_show=True).all() serializer_class = serializers.CourseModelSerializer # 配置自定义过滤组件 filter_backends = [MyFilter] 3. 访问接口的url格式: 127.0.0.1:8000/courses/?count=数字

3|0三、过滤组件之分类与区间

  • 把这两个过滤组件拿出来说,是因为这两个组件需要另外安装django插件来辅助,才能进行

3|11. 分类与区间的使用

1. 安装插件 pip install django-filter 2. 新建filters.py文件,自定义过滤类 from django_filters import filters from . import models class CourseFilterSet(FilterSet): # 实现区间:field_name关联model表属性,lookup_expr设置过滤规则 min_price = filters.NumberFilter(field_name='price', lookup_expr='gte') max_price = filters.NumberFilter(field_name='price', lookup_expr='lte') class Meta: model = models.Course # min_price 和 max_price 自定义规则字段可以不在fields中配置 fields = ['course_category'] 3. views.py文件中,配置过滤组件 *******群查接口的视图类中:********* from django_filters.rest_framework import DjangoFilterBackend from .filters import CourseFilterSet class FreeCourseListViewSet(ListModelMixin, GenericViewSet): queryset = models.Course.objects.filter(is_delete=False, is_show=True).all() serializer_class = serializers.CourseModelSerializer # 配置过滤组件 filter_backends = [DjangoFilterBackend] # 配置过滤规则的类 filter_class = CourseFilterSet # 规则: # ?course_category=1 课程分组1所有的课程 # ?min_price=10 课程价格大于等于10的所有课程 # ?min_price=10&max_price=100 课程价格大于等于10小于等于100的所有课程 4. 访问接口url格式 127.0.0.1:8000/courses/?course_category=数据库中对应的某个值 # 分类 127.0.0.1:8000/courses/?min_price=数字&max_price=数字 # 区间

4|0四、VUE前端播放器组件

  • 使用方法
1. 在main.js文件中配置播放器组件 // vue-video播放器 require('video.js/dist/video-js.css'); require('vue-video-player/src/custom-theme.css'); import VideoPlayer from 'vue-video-player' Vue.use(VideoPlayer); <template> <div class="detail"> <Header/> <div class="main"> <div class="course-info"> <div class="wrap-left"> <!-- *******************播放器组件配置******************************** --> <videoPlayer class="video-player vjs-custom-skin" ref="videoPlayer" :playsinline="true" :options="playerOptions" @play="onPlayerPlay($event)" @pause="onPlayerPause($event)"> </videoPlayer> <!-- *******************播放器组件配置******************************** --> </div> <div class="wrap-right"> <h3 class="course-name">{{course_info.name}}</h3> <p class="data">{{course_info.students}}人在学&nbsp;&nbsp;&nbsp;&nbsp;课程总时长:{{course_info.sections}}课时/{{course_info.pub_sections}}小时&nbsp;&nbsp;&nbsp;&nbsp;难度:{{course_info.level_name}}</p> <div class="sale-time"> <p class="sale-type">价格 <span class="original_price">¥{{course_info.price}}</span></p> <p class="expire"></p> </div> <div class="buy"> <div class="buy-btn"> <button class="buy-now">立即购买</button> <button class="free">免费试学</button> </div> <!--<div class="add-cart" @click="add_cart(course_info.id)">--> <!--<img src="@/assets/img/cart-yellow.svg" alt="">加入购物车--> <!--</div>--> </div> </div> </div> <div class="course-tab"> <ul class="tab-list"> <li :class="tabIndex==1?'active':''" @click="tabIndex=1">详情介绍</li> <li :class="tabIndex==2?'active':''" @click="tabIndex=2">课程章节 <span :class="tabIndex!=2?'free':''">(试学)</span> </li> <li :class="tabIndex==3?'active':''" @click="tabIndex=3">用户评论</li> <li :class="tabIndex==4?'active':''" @click="tabIndex=4">常见问题</li> </ul> </div> <div class="course-content"> <div class="course-tab-list"> <div class="tab-item" v-if="tabIndex==1"> <div class="course-brief" v-html="course_info.brief_text"></div> </div> <div class="tab-item" v-if="tabIndex==2"> <div class="tab-item-title"> <p class="chapter">课程章节</p> <p class="chapter-length">共{{course_chapters.length}}章 {{course_info.sections}}个课时</p> </div> <div class="chapter-item" v-for="chapter in course_chapters" :key="chapter.name"> <p class="chapter-title"><img src="@/assets/img/enum.svg" alt="">第{{chapter.chapter}}章·{{chapter.name}} </p> <ul class="section-list"> <li class="section-item" v-for="section in chapter.coursesections" :key="section.name"> <p class="name"><span class="index">{{chapter.chapter}}-{{section.orders}}</span> {{section.name}}<span class="free" v-if="section.free_trail">免费</span></p> <p class="time">{{section.duration}} <img src="@/assets/img/chapter-player.svg"></p> <button class="try" v-if="section.free_trail">立即试学</button> <button class="try" v-else>立即购买</button> </li> </ul> </div> </div> <div class="tab-item" v-if="tabIndex==3"> 用户评论 </div> <div class="tab-item" v-if="tabIndex==4"> 常见问题 </div> </div> <div class="course-side"> <div class="teacher-info"> <h4 class="side-title"><span>授课老师</span></h4> <div class="teacher-content"> <div class="cont1"> <img :src="course_info.teacher.image"> <div class="name"> <p class="teacher-name">{{course_info.teacher.name}} {{course_info.teacher.title}}</p> <p class="teacher-title">{{course_info.teacher.signature}}</p> </div> </div> <p class="narrative">{{course_info.teacher.brief}}</p> </div> </div> </div> </div> </div> <!--<Footer/>--> </div> </template> <script> import Header from "@/components/Header" // import Footer from "@/components/Footer" // 加载组件 import {videoPlayer} from 'vue-video-player'; export default { name: "Detail", data() { return { tabIndex: 2, // 当前选项卡显示的下标 course_id: 0, // 当前课程信息的ID course_info: { teacher: {}, }, // 课程信息 course_chapters: [], // 课程的章节课时列表 // **********************播放器组件配置********************************** playerOptions: { aspectRatio: '16:9', // 将播放器置于流畅模式,并在计算播放器的动态大小时使用该值。值应该代表一个比例 - 用冒号分隔的两个数字(例如"16:9"或"4:3") sources: [{ // 播放资源和资源格式 type: "video/mp4", src: "http://img.ksbbs.com/asset/Mon_1703/05cacb4e02f9d9e.mp4" //你的视频地址(必填) }], } // **********************播放器组件配置********************************** } }, created() { this.get_course_id(); this.get_course_data(); this.get_chapter(); }, methods: { onPlayerPlay() { // ****************当视频播放时,执行的方法*************** // ****************this.$message('开始播放')************** }, onPlayerPause() { // 当视频暂停播放时,执行的方法 // console.log(this.playerOptions.sources[0].src); // this.playerOptions.sources[0].src = '' // this.$message('视频暂停') }, get_course_id() { // 获取地址栏上面的课程ID this.course_id = this.$route.params.pk; if (this.course_id < 1) { let _this = this; _this.$alert("对不起,当前视频不存在!", "警告", { callback() { _this.$router.go(-1); } }); } }, // 课程单查接口 get_course_data() { // ajax请求课程信息 this.$axios.get(`${this.$settings.base_url}/course/free/${this.course_id}/`).then(response => { // window.console.log(response.data); this.course_info = response.data; }).catch(() => { this.$message({ message: "对不起,访问页面出错!请联系客服工作人员!" }); }) }, // 所有章节所有课时 get_chapter() { // 获取当前课程对应的章节课时信息 // http://127.0.0.1:8000/course/chapters/?course=(pk) this.$axios.get(`${this.$settings.base_url}/course/chapters/`, { params: { "course": this.course_id, } }).then(response => { this.course_chapters = response.data; }).catch(error => { window.console.log(error.response); }) }, }, components: { Header, // Footer, videoPlayer, // 注册组件 } } </script> <style scoped> .main { background: #fff; padding-top: 30px; } .course-info { width: 1200px; margin: 0 auto; overflow: hidden; } .wrap-left { float: left; width: 690px; height: 388px; background-color: #000; } .wrap-right { float: left; position: relative; height: 388px; } .course-name { font-size: 20px; color: #333; padding: 10px 23px; letter-spacing: .45px; } .data { padding-left: 23px; padding-right: 23px; padding-bottom: 16px; font-size: 14px; color: #9b9b9b; } .sale-time { width: 464px; background: #fa6240; font-size: 14px; color: #4a4a4a; padding: 10px 23px; overflow: hidden; } .sale-type { font-size: 16px; color: #fff; letter-spacing: .36px; float: left; } .sale-time .expire { font-size: 14px; color: #fff; float: right; } .sale-time .expire .second { width: 24px; display: inline-block; background: #fafafa; color: #5e5e5e; padding: 6px 0; text-align: center; } .course-price { background: #fff; font-size: 14px; color: #4a4a4a; padding: 5px 23px; } .discount { font-size: 26px; color: #fa6240; margin-left: 10px; display: inline-block; margin-bottom: -5px; } .original { font-size: 14px; color: #9b9b9b; margin-left: 10px; text-decoration: line-through; } .buy { width: 464px; padding: 0px 23px; position: absolute; left: 0; bottom: 20px; overflow: hidden; } .buy .buy-btn { float: left; } .buy .buy-now { width: 125px; height: 40px; border: 0; background: #ffc210; border-radius: 4px; color: #fff; cursor: pointer; margin-right: 15px; outline: none; } .buy .free { width: 125px; height: 40px; border-radius: 4px; cursor: pointer; margin-right: 15px; background: #fff; color: #ffc210; border: 1px solid #ffc210; } .add-cart { float: right; font-size: 14px; color: #ffc210; text-align: center; cursor: pointer; margin-top: 10px; } .add-cart img { width: 20px; height: 18px; margin-right: 7px; vertical-align: middle; } .course-tab { width: 100%; background: #fff; margin-bottom: 30px; box-shadow: 0 2px 4px 0 #f0f0f0; } .course-tab .tab-list { width: 1200px; margin: auto; color: #4a4a4a; overflow: hidden; } .tab-list li { float: left; margin-right: 15px; padding: 26px 20px 16px; font-size: 17px; cursor: pointer; } .tab-list .active { color: #ffc210; border-bottom: 2px solid #ffc210; } .tab-list .free { color: #fb7c55; } .course-content { width: 1200px; margin: 0 auto; background: #FAFAFA; overflow: hidden; padding-bottom: 40px; } .course-tab-list { width: 880px; height: auto; padding: 20px; background: #fff; float: left; box-sizing: border-box; overflow: hidden; position: relative; box-shadow: 0 2px 4px 0 #f0f0f0; } .tab-item { width: 880px; background: #fff; padding-bottom: 20px; box-shadow: 0 2px 4px 0 #f0f0f0; } .tab-item-title { justify-content: space-between; padding: 25px 20px 11px; border-radius: 4px; margin-bottom: 20px; border-bottom: 1px solid #333; border-bottom-color: rgba(51, 51, 51, .05); overflow: hidden; } .chapter { font-size: 17px; color: #4a4a4a; float: left; } .chapter-length { float: right; font-size: 14px; color: #9b9b9b; letter-spacing: .19px; } .chapter-title { font-size: 16px; color: #4a4a4a; letter-spacing: .26px; padding: 12px; background: #eee; border-radius: 2px; display: -ms-flexbox; display: flex; -ms-flex-align: center; align-items: center; } .chapter-title img { width: 18px; height: 18px; margin-right: 7px; vertical-align: middle; } .section-list { padding: 0 20px; } .section-list .section-item { padding: 15px 20px 15px 36px; cursor: pointer; justify-content: space-between; position: relative; overflow: hidden; } .section-item .name { font-size: 14px; color: #666; float: left; } .section-item .index { margin-right: 5px; } .section-item .free { font-size: 12px; color: #fff; letter-spacing: .19px; background: #ffc210; border-radius: 100px; padding: 1px 9px; margin-left: 10px; } .section-item .time { font-size: 14px; color: #666; letter-spacing: .23px; opacity: 1; transition: all .15s ease-in-out; float: right; } .section-item .time img { width: 18px; height: 18px; margin-left: 15px; vertical-align: text-bottom; } .section-item .try { width: 86px; height: 28px; background: #ffc210; border-radius: 4px; font-size: 14px; color: #fff; position: absolute; right: 20px; top: 10px; opacity: 0; transition: all .2s ease-in-out; cursor: pointer; outline: none; border: none; } .section-item:hover { background: #fcf7ef; box-shadow: 0 0 0 0 #f3f3f3; } .section-item:hover .name { color: #333; } .section-item:hover .try { opacity: 1; } .course-side { width: 300px; height: auto; margin-left: 20px; float: right; } .teacher-info { background: #fff; margin-bottom: 20px; box-shadow: 0 2px 4px 0 #f0f0f0; } .side-title { font-weight: normal; font-size: 17px; color: #4a4a4a; padding: 18px 14px; border-bottom: 1px solid #333; border-bottom-color: rgba(51, 51, 51, .05); } .side-title span { display: inline-block; border-left: 2px solid #ffc210; padding-left: 12px; } .teacher-content { padding: 30px 20px; box-sizing: border-box; } .teacher-content .cont1 { margin-bottom: 12px; overflow: hidden; } .teacher-content .cont1 img { width: 54px; height: 54px; margin-right: 12px; float: left; } .teacher-content .cont1 .name { float: right; } .teacher-content .cont1 .teacher-name { width: 188px; font-size: 16px; color: #4a4a4a; padding-bottom: 4px; } .teacher-content .cont1 .teacher-title { width: 188px; font-size: 13px; color: #9b9b9b; white-space: nowrap; } .teacher-content .narrative { font-size: 14px; color: #666; line-height: 24px; } </style>

__EOF__

本文作者BigSun丶
本文链接https://www.cnblogs.com/Mcoming/p/12189296.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   BigSun丶  阅读(247)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
点击右上角即可分享
微信分享提示