这篇属于Django Rest Framework系列,但我忘了发表,现在补回来。
一、过滤filtering
RESTful API规范之一是要有数据筛选功能,即只输出指定的数据,这需要后端提供过滤功能,我们可以通过使用第三方模块django-filter来达到目的。
1.1、filter安装配置
1.1.1、安装
pip3 install django-filter # 需要在settings的APP中注册
1.1.2、注册过滤器
INSTALLED_APPS = [ # 注册
'django_filters'
]
REST_FRAMEWORK = { # 启用
'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',)
}
1.2、使用filter
涉及到models、views、序列化器等。
1.2.1、models代码
写好代码后迁移以创建表结构
from django.db import models
# Create your models here.
class Authors(models.Model):
name = models.CharField(max_length=16, verbose_name='作者')
gender = models.IntegerField(choices=((1, 'Male'), (2, 'Female')), verbose_name='性别')
def __str__(self):
return self.name
class Meta(object):
verbose_name_plural = '作者'
class Books(models.Model):
name = models.CharField(max_length=16, verbose_name='书名')
price = models.DecimalField(max_digits=6, decimal_places=2, verbose_name='价格')
# 为什么用DO_NOTHING?如果用CASCADE,一旦删除数据,关联的所有数据也会被一并删除。
author = models.ForeignKey(to=Authors, on_delete=models.DO_NOTHING)
def __str__(self):
return self.name
class Meta(object):
verbose_name_plural = '书籍'
1.2.2、序列化器代码
from rest_framework import serializers
from just_test import models
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = models.Books
fields = '__all__'
# 因为Book中author是外键字段,GET请求到的是ID号,我们要把它转成实际对应的名称提高用户体验。
author = serializers.CharField(source='author.name')
extra_kwargs = {
'author': 'write_only', # 把GET到的author的ID号隐藏掉,只剩下上面定义的author。
}
1.2.3、views代码
from rest_framework.generics import ListAPIView
from just_test.models import Books
from utils.myserializer import BookSerializer
# Create your views here.
class BookView(ListAPIView):
queryset = Books.objects.all()
serializer_class = BookSerializer
filter_fields = ('price',)
1.2.4、urls代码
from django.contrib import admin
from django.urls import path
from just_test import views
urlpatterns = [
path('admin/', admin.site.urls),
path('api/books/', views.BookView.as_view())
]
1.3、验证效果
未过滤
postman访问:127.0.0.1:8000/api/books/
[
{
"id": 1,
"author": "三溪",
"name": "津门除妖记",
"price": "10.00"
},
{
"id": 2,
"author": "三溪",
"name": "今村异闻录",
"price": "8.00"
},
{
"id": 3,
"author": "刘慈欣",
"name": "三体",
"price": "21.00"
},
{
"id": 4,
"author": "刘慈欣",
"name": "流浪地球",
"price": "16.00"
}
]
过滤
postman访问:127.0.0.1:8000/api/books/?price=21
[
{
"id": 3,
"author": "刘慈欣",
"name": "三体",
"price": "21.00"
}
]
二、排序
还是以上面的数据为基础做实验。
2.1、直接排序
rest_framework有自带的过滤模块OrderingFilter,但是OrderingFilter跟上面不一样,它是排序模块。请求时,rest_framework会检查url后面有没有带ordering参数,有则按照ordering指定字段进行排序。
2.1.1、views代码
from rest_framework.generics import ListAPIView
from just_test.models import Books
from utils.myserializer import BookSerializer
from rest_framework.filters import OrderingFilter # 导入模块
# Create your views here.
class BookView(ListAPIView):
queryset = Books.objects.all()
serializer_class = BookSerializer
filter_backends = [OrderingFilter] # 启用OrderingFilter模块
ordering_fields = ('price', 'id') # 指定排序字段
2.1.2、验证效果
postman访问:127.0.0.1:8000/api/books/?ordering=-price,得到结果是按照价格由高到低进行排序。
[
{
"id": 3,
"author": "刘慈欣",
"name": "三体",
"price": "21.00"
},
{
"id": 4,
"author": "刘慈欣",
"name": "流浪地球",
"price": "16.00"
},
{
"id": 1,
"author": "三溪",
"name": "津门除妖记",
"price": "10.00"
},
{
"id": 2,
"author": "三溪",
"name": "今村异闻录",
"price": "8.00"
}
]
三、分页
当API查询请求数据量比较大的时候,一次性返回就不太现实也不合理,应该提供一种分页展示的方法供用户选择一页展示多少条。
3.1、全局分页
3.1.1、settings配置
全局分页是最简单的,因为rest_framework有内置模块,只需要在settings里面配置如下两行代码即可:
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 3 # 每页数目
}
3.1.2、验证效果
postman访问:127.0.0.1:8000/api/books/
{
"count": 4,
"next": "http://127.0.0.1:8000/api/books/?page=2",
"previous": null,
"results": [
{
"id": 1,
"author": "三溪",
"name": "津门除妖记",
"price": "10.00"
},
{
"id": 2,
"author": "三溪",
"name": "今村异闻录",
"price": "8.00"
},
{
"id": 3,
"author": "刘慈欣",
"name": "三体",
"price": "21.00"
}
]
}
3.2、自定义分页类
自定义分页类用法也非常简单,几行代码即可搞掂!
3.2.1、自定义分页类
from rest_framework.pagination import PageNumberPagination # 导入rest_framework分页类
class MyPageNumberPagination(PageNumberPagination): # 自定义类必须继承该类
page_size = 3 # 默认一页展示的数据条数
page_query_param = 'page' # 自定义分页关键字
page_size_query_param = 'size' # 每页展示条数,但不能超过max_page_size。
max_page_size = 10 # 每页最大展示条数
3.2.2、views代码
from rest_framework.generics import ListAPIView
from just_test.models import Books
from utils.myserializer import BookSerializer
from utils.mypagination import MyPageNumberPagination # 导入自定义的分页类
# Create your views here.
class BookView(ListAPIView):
queryset = Books.objects.all()
serializer_class = BookSerializer
pagination_class = MyPageNumberPagination # 启用自定义分页类
3.2.3、验证效果
默认分页
postman访问:127.0.0.1:8000/api/books/,此为使用后端默认配置,返回结果如下所示:
{
"count": 7,
"next": "http://127.0.0.1:8000/api/books/?page=2",
"previous": null,
"results": [
{
"id": 1,
"author": "三溪",
"name": "津门除妖记",
"price": "10.00"
},
{
"id": 2,
"author": "三溪",
"name": "今村异闻录",
"price": "8.00"
},
{
"id": 3,
"author": "刘慈欣",
"name": "三体",
"price": "21.00"
}
]
}
自定义分页
postman访问:127.0.0.1:8000/api/books/?page=2&size=3,此为每页展示三条,且只访问第二页的那三条:
{
"count": 7,
"next": "http://127.0.0.1:8000/api/books/?page=3&size=3",
"previous": "http://127.0.0.1:8000/api/books/?size=3",
"results": [
{
"id": 4,
"author": "刘慈欣",
"name": "流浪地球",
"price": "16.00"
},
{
"id": 5,
"author": "金庸",
"name": "笑傲江湖",
"price": "16.00"
},
{
"id": 6,
"author": "金庸",
"name": "越女剑",
"price": "11.00"
}
]
}
3.3、游标分页类
上面的自定义普通分页类可以指定访问第几页,假设要查询第1000页的数据,它是一页一页地查,直到第1000页再把那一页给你拿出来;就好比翻书,它是一页一页地翻到第1000页书再展示给你看,效率可想而知!
Cursor,游标,这个分页类比较特殊。特殊在它是按照字段先进行排序,排序后只能一页一页地翻,不能跳转,因为不需要像上面的普通分页一下子跳很多页,所以效率相比很高,但是用户得一页一页地点击查看。
它速度快,但缺点页很明显,用户无法控制,所有参数都在后台写死了的,因此仅适用于某些固定场景!
3.3.1、游标分页代码
from rest_framework.pagination import CursorPagination
class MyPageNumberPagination(CursorPagination):
cursor_query_param = 'cursor' # 分页关键字,前端只能看,无法改。
page_size = 3 # 每页显示条数
ordering = 'price' # 排序的字段,固定死的。
3.3.2、views代码
from rest_framework.generics import ListAPIView
from just_test.models import Books
from utils.myserializer import BookSerializer
from utils.mypagination import MyPageNumberPagination
# Create your views here.
class BookView(ListAPIView):
queryset = Books.objects.all()
serializer_class = BookSerializer
pagination_class = MyPageNumberPagination
3.3.3、验证效果
postman或浏览器访问:127.0.0.1:8000/api/books/,得到结果如下所示:
{ # 只能点击next跳到下一页,其它所有操作都不行。
"next": "http://127.0.0.1:8000/api/books/?cursor=cD0xMS4wMA%3D%3D",
"previous": null,
"results": [
{
"id": 2,
"author": "三溪",
"name": "今村异闻录",
"price": "8.00"
},
{
"id": 1,
"author": "三溪",
"name": "津门除妖记",
"price": "10.00"
},
{
"id": 6,
"author": "金庸",
"name": "越女剑",
"price": "11.00"
}
]
}
四、异常处理
以往我们程序遇到故障时,会直接报错并把错误代码一并展示出来,但很多时候我们并不希望被用户看到这些错误信息和代码,所以我们需要自定义异常处理功能。
未完待续
REST framework为我们提供了异常处理模块exception_handler来自定义异常处理功能。
作用,统一错误信息的返回,记录日志