序列化
from rest_framework import serializers
from .models import Hosts, HostsInfo
class HostsInfoSerializer(serializers.ModelSerializer):
class Meta:
model = HostsInfo
fields = ('position', 'is_used')
class HostsSerializer(serializers.ModelSerializer):
# info = HostsInfoSerializer()
class Meta:
# 操作的模型
model = Hosts
# 序列化后输出的字段
fields = '__all__'
# 额外字段, 在序列化的时候不会体现出来
extra_kwargs = {
'ip': {'min_value': 0, 'max_value': 1000}
}
# 只读 => 序列化 (不允许反序列化)
read_only_fileds = ['ip', 'serial_number']
序列化
Serializer(instance=None, data=None, **kwargs)
from .serializers import HostsSerializer
from .models import Hosts
host = Hosts.objects.get(id=1)
# HostsInfoSerializer(实例, many=True)
HostsInfoSerializer(host).data # 可获取到Json数据
# 当实例是一个QuerySet的时候需要加上many=True
HostsInfoSerializer(Hosts.objects.all(). many=True).data
反序列化
from hosts.models import Hosts
from hosts.serializers import HostsSerializer
"""
流程
拿到前端传入的数据 --> 写入到序列化器的data属性 --> 调用序列化器的.is_valid()进行校验 --> 调用序列化器的save()方法
"""
data = {
'ip': '192.168.1.1',
'serial_number': '12334',
'asset_number': '12334',
}
s = HostsSerializer(data=data)
s.is_valid() # => 无效返回False, 反之为True
# s.is_valid(raise_exception=True) # => 直接抛出错误信息
s.errors # => 可以获取到校验失败的错误信息
s.save() # => 保存到模型 他可能执行 .create 或 .update 方法
"""
HostsSerializer(instance=MODEL, data=data)
# 同时具备instance和data, 那么save会执行update
# 只有data, 那么save执行的是create
# context:dict 额外数据
"""
# 获取反序列化的数据
s.validated_data # => OrderDict
在基类Serializer
中, 没有实现update
方法, 如果继承自Serializer
, 需要自己实现update
方法
class HostsSerializer(serializers.Serializer):
# info = HostsInfoSerializer()
ip = serializers.CharField(label='ID', read_only=True)
serial_number = serializers.CharField(label='序列号', validators=[check, ])
asset_number = serializers.CharField(label='资产编码')
is_delete = serializers.BooleanField(label='逻辑删除')
class Meta:
model = Hosts
fields = '__all__'
@classmethod
def validate_serial_number(cls, value):
if len(value) < 5:
raise serializers.ValidationError('序列号长度不足5')
return value
def validate(self, attrs: dict):
# 联合校验
print(attrs) # 所有数据, 可以对多个字段做校验
return super(HostsSerializer, self).validate(attrs)
def update(self, instace, validate_data):
# validate_data就是 self.validate_data
# instace 就是 self.instance
pass
def create(self, validated_data):
pass
追加额外的校验逻辑
def check(val):
if len(val) < 5:
raise serializers.ValidationError('长度小于5')
return val
class HostsSerializer(serializers.ModelSerializer):
# info = HostsInfoSerializer()
ip = serializers.CharField(label='ID', read_only=True)
# 也可以在字段加入validators=[校验函数, 校验函数,]
serial_number = serializers.CharField(label='序列号', validators=[check, ])
asset_number = serializers.CharField(label='资产编码')
is_delete = serializers.BooleanField(label='逻辑删除')
class Meta:
model = Hosts
fields = '__all__'
@classmethod
def validate_serial_number(cls, value):
if len(value) < 5:
raise serializers.ValidationError('序列号长度不足5')
return value
def validate(self, attrs: dict):
# 联合校验
print(attrs) # 所有数据, 可以对多个字段做校验
return super(HostsSerializer, self).validate(attrs)
视图
Request
REST
中的Request
对象不再是Django
默认的的HttpRequest
对象, 而是REST
提供了基于Django HttpRquest扩展的Request
REST Framework提供了Parser解析器, 在接收到请求后会自动根据Content-Type
指明的请求数据进行解析, 解析为类字典保存到Request
中, 这样我们就可以用统一的方法读取请求中的数据
常用属性:
- data: 请求的数据
- query_params: 查询字符串
Response
rest_framework.response.Response
REST Framework提供了响应类Response
,使用该类构造响应对象时, 响应的具体数据内容会被转换为(reader
渲染)成符合前端需求的类型
RESET Framework提供了Renderer
渲染器, 用来根据请求头中的Accept
(接收数据类型声明)来自动转换响应数据到对应的格式, 如果前端请求中未进行Accept
声明, 则会采用默认方式处理响应数据, 我们可以通过配置来修改默认响应格式
# setting.py
REST_FRAMEWORK = {
# 定义默认的渲染器
'DEFAULT_RENDERER_CLASSES': (
'utils.render.ITJsonRenderer',
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.BrowsableAPIRenderer',
),
}
响应构造方法
Response(data, status=None, template_name=None, headers=None, content_type=None)
"""
data: 为响应准备的序列化处理后的数据
status: 状态码, 默认是200
template_name: 模版名称, 如果使用HTMLRenderer需要指明该参数
header: 响应头
content_type: 响应数据类型, 通常无需床底, REST会根据前端需要的数据类型自动设置
"""
常用属性:
- data: 序列化后的数据, 还没有被renderer处理的数据
- status_code: 状态码
- content: 经过renderer处理的数据
在Response中,REST将常用的状态码定义为了常量
from rest_framework import status
# 消息告知 1XX
HTTP_100_CONTINUE = 100
HTTP_101_SWITCHING_PROTOCOLS = 101
# 成功 2XX
HTTP_200_OK = 200
HTTP_201_CREATED = 201
HTTP_202_ACCEPTED = 202
HTTP_203_NON_AUTHORITATIVE_INFORMATION = 203
HTTP_204_NO_CONTENT = 204
HTTP_205_RESET_CONTENT = 205
HTTP_206_PARTIAL_CONTENT = 206
# 资源重定向 3XX
HTTP_300_MULTIPLE_CHOICES = 300
HTTP_301_MOVED_PERMANENTLY = 301
HTTP_302_FOUND = 302
HTTP_303_SEE_OTHER = 303
HTTP_304_NOT_MODIFIED = 304
HTTP_305_USE_PROXY = 305
HTTP_306_RESERVED = 306
HTTP_307_TEMPORARY_REDIRECT = 307
# HTTP错误 4XX
HTTP_400_BAD_REQUEST = 400
HTTP_401_UNAUTHORIZED = 401
HTTP_402_PAYMENT_REQUIRED = 402
HTTP_403_FORBIDDEN = 403
HTTP_404_NOT_FOUND = 404
HTTP_405_METHOD_NOT_ALLOWED = 405
HTTP_406_NOT_ACCEPTABLE = 406
HTTP_407_PROXY_AUTHENTICATION_REQUIRED = 407
HTTP_408_REQUEST_TIMEOUT = 408
HTTP_409_CONFLICT = 409
HTTP_410_GONE = 410
HTTP_411_LENGTH_REQUIRED = 411
HTTP_412_PRECONDITION_FAILED = 412
HTTP_413_REQUEST_ENTITY_TOO_LARGE = 413
HTTP_414_REQUEST_URI_TOO_LONG = 414
HTTP_415_UNSUPPORTED_MEDIA_TYPE = 415
HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE = 416
HTTP_417_EXPECTATION_FAILED = 417
HTTP_422_UNPROCESSABLE_ENTITY = 422
HTTP_423_LOCKED = 423
HTTP_424_FAILED_DEPENDENCY = 424
HTTP_428_PRECONDITION_REQUIRED = 428
HTTP_429_TOO_MANY_REQUESTS = 429
HTTP_431_REQUEST_HEADER_FIELDS_TOO_LARGE = 431
HTTP_451_UNAVAILABLE_FOR_LEGAL_REASONS = 451
# 服务器错误 5XX
HTTP_500_INTERNAL_SERVER_ERROR = 500
HTTP_501_NOT_IMPLEMENTED = 501
HTTP_502_BAD_GATEWAY = 502
HTTP_503_SERVICE_UNAVAILABLE = 503
HTTP_504_GATEWAY_TIMEOUT = 504
HTTP_505_HTTP_VERSION_NOT_SUPPORTED = 505
HTTP_507_INSUFFICIENT_STORAGE = 507
HTTP_511_NETWORK_AUTHENTICATION_REQUIRED = 511
两个视图基类
APIView
from rest_framework.views import APIView
APIView
是REST
提供的所有视图的基类, 继承自Django
的View
APIView
与View
的不同之处
- 传入到视图方法中的是
REST
的Request
对象, 而不是Django
的HttpRequest
对象 - 视图方法可以放回
REST
的Rsponse
对象, 视图会为响应数据设置(redner
)符合前端要求的格式 - 任何
APIException
异常都会被捕获到, 并处理成合适的响应信息 - 在进行
dispatch
分发前, 会对请求进行身份认证/权限检查, 流量控制
支持定义的属性
authentication_classes
列表或元组, 身份认证类permission_classes
列表或元组, 权限检查类throttle_classes
列表或元组, 流量控制类
在APIView
中仍以常规的类视图定义方法来实现get(), post()
等其他请求方法
举例:
class HostsView(APIView):
"""列表视图"""
def get(self, request: Request, pk=None):
if pk:
query = HostsSerializer(Hosts.objects.filter(id=pk), many=True)
else:
query = HostsSerializer(Hosts.objects.all(), many=True)
data = {
'code': 200,
'data': query.data
}
return Response(data)
def post(self, requset):
pass
# 几乎和原生的Django框架使用方法一致
GenericAPIView
from rest_framework.generics import GenericAPIView
继承自APIView
,主要增加了操作序列化和数据库查询的方法, 作用是为一下Mixin
扩展类的执行提供语法资产, 通常在使用时,可以搭配一个或多个Minin
扩展类.
提供的关于序列化器使用的属性和方法
属性
serializer_class
: 指明视图的序列化器queryset
: 指明查询集lookup_field
: 默认是pk
, 在get_object()
中有是使用到
方法
get_serializer_class(self)
返回序列化器类, 默认返回
serialzer_class
, 可以对其进行重写get_serializer(self, *arg, **kwargs)
获取序列化器, 可像序列化器一样调用它
提供了关于数据库查询的属性和方法
属性
queryset
指明使用的数据查询集
方法
get_queryset(self)
返回视图使用的查询集,主要用来提供给
Mixin
扩展类使用, 是列表视图于详情视图获取数据的基础, 默认返回queryset
属性, 同样可以重写get_object(self)
获取单一的数据对象, 如详情访问的模型类对象不存在, 会返回404, 默认会过滤一个
pk
, 如果pk
存在其他可以设置的属性
pagination_class
: 指明分页控制类filter_backends
: 指明过滤控制后端
五个扩展类
from rest_framework import mixins
mixins.ListModelMixin # => 这一个主要是返回一个list
mixins.CreateModelMixin # => 创建扩展POST
mixins.RetrieveModelMixin # => GET 获取单个资源
mixins.UpdateModelMixin # => PUT 修改单个资源
mixins.DestroyModelMixin # =>DELETE 删除单个资源
其他视图
from rest_framework.generics import ListAPIView
# 这个视图只提供了查询所有资源
from rest_framework.generics import CreateAPIView
# 这个视图只提供了Create资源
from rest_framework.generics import ListCreateAPIView
# 这个视图拥有查询所有和新增资源
RetrieveAPIView # 查询单一
DestroyAPIView # 删除单一
ModelViewSet
from rest_framework.viewsets import ModelViewSet
# 继承了五个Mixin扩展和GenericViewSet
视图集
ViewSet
from rest_framework.viewsets import ViewSet
使用视图集ViewSet
, 可以将一系列逻辑相关的动作放到一个类中
list()
提供一组数据retrieve()
提供单个数据create()
创建数据update()
更新数据destory()
删除/销毁数据
ViewSet
视图集不在实现get(), post()
等方法, 而是实现动作action
如list(), create()
等, 视图集只使用在使用as_view()
方法到时候, 才会将action
动作与具体请求方式对应上.如:
class HostsView(viewsets.ViewSet):
def list(self, request):
hosts = Host.objects.all()
serializer = HostsSerializer(hosts, many=True)
return Response(serializer.data)
def retrieve(self, requset, pk=None):
try:
host = Host.objects.get(id=pk)
except Host.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
serializer = HostsSerializer(host)
return Response(serializer.data)
# 设置路由的时候应该设置如下
urlpatterns = [
# 需要在as_view()中写入一个字典, 定义get/post请求对应的方法
url(r'^hosts/$', HostsView.as_view({'get': 'list'})),
]
GenericViewSet
from rest_framework.viewsets import GenericViewSet
# GenericViewSet 继承自 GenericView
# 使用方法一致
# as_view的时候指定请求方式对应的方法
ModelViewSet
from rest_framework.viewsets import ModelViewSet
# ModelViewSet继承自GenericViewSet和五个扩展类
# 默认就存在list, update, create, retrieve, destroy
在是视图集中添加额外的行为
# 例子
class HostsView(ModelViewSet):
"""获取最后一个机器"""
queryset = Hosts.objects.all()
serializer_class = HostsSerializer
def latest(self, request):
pass
urlpatterns = [
# 获取最后一台机器
url(r'^hosts/latest/$', HostsView.as_view({'get': 'latest'})),
]
# 简化操作后
# 路由器只能结合视图集使用
from rest_framework.decorators import list_route, detail_route
class HostsView(ModelViewSet):
"""获取最后一个机器"""
queryset = Hosts.objects.all()
serializer_class = HostsSerializer
@list_route(method=['get'])
def latest(self, request):
pass
@detail_route(method=['put'])
def read(request):
pass
urlpatterns = [
# 获取最后一台机器
url(r'^hosts/$', HostsView.as_view()),
]
# list_route 列表路由
# -> 如果有pk,路由大概为: /hosts/latest/(?P<pk>\d+)$/
# -> 没有pk, 路由大概为: /hosts/latest/
# detail_route 详情路由
# -> 路由大概为: /hosts/(?P<pk>\d+)$/latest/
分页和过滤
路由
DefaultRouter
和SimpleRouter
的区别.当项目跟路由中出现了其他路由, DefaultRouter
会报错404
, 也就是DefaultRouter
会比SimpleRouter
多生成一个根路由, SimpleRouter
则不会影响.
认证
可以在配置文件中配置全局认证方案
# setting.py
#
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.SessionAuthentication', # Session认证
'rest_framework.authentication.BasicAuthentication', # 基本认证
],
}
# 这个视图只只会走REST框架视图
也可以在视图中通过设置authentication_classess
属性设置
from rest_framework.authentication import SessionAuthentication, BaseAuthentication
class UserInfo(APIView):
authentication_classes = (SessionAuthentication, BaseAuthentication)
# ....
# 不过现在主要是使用JWT认证
401 Unauthorized
或403 Permission Denied
权限
使用
在配置文件中设置默认的权限管理类, 如:
# setting.py
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated',
],
}
# 如果没有指明, 则采用下面的默认配置. 权限指定局部的, 不要全局指定
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.AllowAny',
],
}
也可以在视图中通过permission_classess
设置
提供的权限
AllowAny
: 允许所有用户IsAuthenticated
: 仅允许通过认证的永华IsAdmin
: 仅管理员用户IsAuthenticatedOrReadOnly
:认证的用户可以完全操作, 否者只能get
举例
from rest_framework.permissions import IsAuthenticated
class HostsView(ModelViewSet):
queryset = Hosts.objects.all()
serializer_class = HostsSerializer
authentication_classes = [SessionAuthentication]
permission_classes = [IsAuthenticated]
# ...
自定义权限
class MyPermission(BasePermission):
def has_object_permission(self, request, view, obj):
"""
是否可以访问数据对象/详情视图
:param request: 请求
:param view: 视图
:param obj: 数据对象
:return:
"""
return False
def has_permission(self, request, view):
"""
是否可以访问视图/数据列表
:param request: 请求
:param view: 视图
:return:
"""
return False
如果全局设置了都必须登陆才能访问视图, 某一个视图不需要登陆
class MyView(APIView):
def check_permissions(self, request):
"""在视图中重写该方法"""
pass
限流
可以对接口的访问进行一定的访问控制, 以减轻服务器压力
# setting.py
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': (
'rest_framework.throttling.AnonRateThrottle', # 匿名用户
'rest_framework.throttling.UserRateThrottle', # 登陆用户
),
'DEFAULT_THROTTLE_RATES': {
'anon': '100/day', # 匿名用户
'user': '1000/day', # 登陆用户
},
}
DEFAULT_THROTTLE_RATES
可以使用second, minute,hour
或day
来指明周期, 也可以在具体视图中通过throttle_classess
属性来配置. 如:
class MyView(APIView):
throttle_classess = [UserRateThrottle]
过滤Filtering
对于列表数据可能需要根据字段进行过滤,我们可以通过添加django-fitlter扩展来增强支持。
pip install django-filter
在配置文件中增加过滤后端的设置:
# setting.py
INSTALLED_APPS = [
...
'django_filters', # 需要注册应用,
]
REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',)
}
在视图中添加filter_fields属性,指定可以过滤的字段
class BookListView(ListAPIView):
queryset = BookInfo.objects.all()
serializer_class = BookInfoSerializer
filter_fields = ('btitle', 'bread')
# 127.0.0.1:8000/book/?btitle=
排序
对于列表数据,REST framework提供了OrderingFilter 过滤器来帮助我们快速指明数据按照指定字段进行排序。
使用方法:
在类视图中设置filter_backends,使用rest_framework.filters.OrderingFilter
过滤器,REST framework会在请求的查询字符串参数中检查是否包含了ordering参数,如果包含了ordering参数,则按照ordering参数指明的排序字段对数据集进行排序。
前端可以传递的ordering参数的可选字段值需要在ordering_fields中指明。
示例:
class BookListView(ListAPIView):
queryset = BookInfo.objects.all()
serializer_class = BookInfoSerializer
filter_backends = [OrderingFilter]
ordering_fields = ('id', 'bread', 'bpub_date')
# 127.0.0.1:8000/books/?ordering=-bread
分页
REST framework提供了分页的支持。
我们可以在配置文件中设置全局的分页方式,如:
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 100 # 每页数目
}
也可通过自定义Pagination类,来为视图添加不同分页行为。在视图中通过pagination_class
属性来指明。
class LargeResultsSetPagination(PageNumberPagination):
page_size = 1000
page_size_query_param = 'page_size'
max_page_size = 10000
class BookDetailView(RetrieveAPIView):
queryset = BookInfo.objects.all()
serializer_class = BookInfoSerializer
pagination_class = LargeResultsSetPagination
注意:如果在视图内关闭分页功能,只需在视图内设置
pagination_class = None
可选分页器
1) PageNumberPagination
前端访问网址形式:
GET http://api.example.org/books/?page=4
可以在子类中定义的属性:
- page_size 每页数目
- page_query_param 前端发送的页数关键字名,默认为"page"
- page_size_query_param 前端发送的每页数目关键字名,默认为None
- max_page_size 前端最多能设置的每页数量
from rest_framework.pagination import PageNumberPagination
class StandardPageNumberPagination(PageNumberPagination):
page_size_query_param = 'page_size'
max_page_size = 10
class BookListView(ListAPIView):
queryset = BookInfo.objects.all().order_by('id')
serializer_class = BookInfoSerializer
pagination_class = StandardPageNumberPagination
# 127.0.0.1/books/?page=1&page_size=2
2)LimitOffsetPagination
前端访问网址形式:
GET http://api.example.org/books/?limit=100&offset=400
可以在子类中定义的属性:
- default_limit 默认限制,默认值与
PAGE_SIZE
设置一致 - limit_query_param limit参数名,默认'limit'
- offset_query_param offset参数名,默认'offset'
- max_limit 最大limit限制,默认None
from rest_framework.pagination import LimitOffsetPagination
class BookListView(ListAPIView):
queryset = BookInfo.objects.all().order_by('id')
serializer_class = BookInfoSerializer
pagination_class = LimitOffsetPagination
# 127.0.0.1:8000/books/?offset=3&limit=2