序列化

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

APIViewREST提供的所有视图的基类, 继承自DjangoView

APIViewView的不同之处

  • 传入到视图方法中的是RESTRequest对象, 而不是DjangoHttpRequest对象
  • 视图方法可以放回RESTRsponse对象, 视图会为响应数据设置(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()等方法, 而是实现动作actionlist(), 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/

分页和过滤

路由

DefaultRouterSimpleRouter的区别.当项目跟路由中出现了其他路由, 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 Unauthorized403 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,hourday来指明周期, 也可以在具体视图中通过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

异常处理

自动生成接口文档

最后修改:2021 年 06 月 21 日 10 : 35 PM
如果觉得我的文章对你有用,请随意赞赏