码迷,mamicode.com
首页 > 其他好文 > 详细

django-rest-framework-源码解析002-序列化/请求模块/响应模块/渲染模块/十大接口

时间:2020-07-16 00:25:06      阅读:153      评论:0      收藏:0      [点我收藏+]

标签:script   empty   查询   func   desc   charset   优先   dump   基类   

简介

当我们使用django-rest-framework框架时, 项目必定是前后端分离的, 那么前后端进行数据交互时, 常见的数据类型就是xml和json(现在主流的是json), 这里就需要我们django后台对json和python字典(dict)进行频繁的转化, 当然我们可以使用json模块的loads和dumps方法去手动转换, 但是这样的操作步骤固定且频繁, 于是可以将这个转化=换步骤进行封装, 让我们实际开发时无需在数据转换上花太多的时间.

rest_framework模块就提供了序列化器这个功能, 专门用来处理数据转换, 将python格式的数据转化为json被称为序列化, 一般是用在返回给前端时使用. 将json数据转化为python格式的数据被称为反序列化, 一般是用在接收前端提交的数据时使用, 且我们一般都要对前台提供过来的数据进行校验, 序列化器中也提供了校验相关的hook, 可以理解为提供了让我们编写自定义的校验函数的位置

rest_framework提供了多个序列化器类供我们使用, 他们都在rest_framework.serializers中:

  Serializer: 是DRF提供的序列化基本类, 需要自己编写所有的字段以及create和update方法,比较底层,抽象度较低,接近Django 的form表单类的层次。

  ModelSerializer: 更常用的序列化类, 无需自己编写字段以及create和update方法, 它会根据指向的model,自动生成默认的 字段和简单的create及update方法。

  ListSerializer: 一般直接使用的比较少, 需要使用到的多数情况是要定制ListSerializer行为, 如当需要批量更新时, 就需要额外定义一个ListSerializer类来重写update方法

  HyperlinkedModelSerializer: 类似于ModelSerializer类,不同之处在于它使用超链接来表示关联关系而不是主键。默认情况下序列化器将包含一个url字段而不是主键字段。

Serializer

定义Serializer类, 需要定义待序列化或反序列化的字段, 重写create和update方法

from rest_framework import serializer
from app.models import Comment

class CommentSerializer(serializers.Serializer):
    email = serializers.EmailField()
    content = serializers.CharField(max_length=200)
    created = serializers.DateTimeField()

    def create(self, validated_data):
        return Comment(**validated_data)

    def update(self, instance, validated_data):
        instance.email = validated_data.get(email, instance.email)
        instance.content = validated_data.get(content, instance.content)
        instance.created = validated_data.get(created, instance.created)
        return instance

ModelSerializer和ListSerializer

定义ModelSerializer类

from rest_framework.serializers import ModelSerializer, ListSerializer
from rest_framework.exceptions import ValidationError
from books.models import Book, Author, AuthorDetail, Publish

# 群改调用update时, 需要通过 ListSerializer 重写update方法才能做更新操作
# instance为需要修改的对象列表, validated_data为对应的更新后的数据
class V2BookListSerializer(ListSerializer):
    def update(self, instance, validated_data):
        for index, obj in enumerate(instance):
            self.child.update(instance=obj, validated_data=validated_data[index])
        return instance


class V2BookSerializer(ModelSerializer):
    class Meta:
        model = Book
        # 序列化和反序列化的字段都合并到了fields中
        fields = [name, price, img, author_list, publish_name, publish, authors]
        # 所有字段
        # fields = ‘__all__‘
        # 除去这些字段不展示
        # exclude = [‘id‘, ‘is_delete‘, ‘create_time‘]
        # 自动展示深度
        # depth = 1
        # 通过write_only设置只参与反序列化, read_only只参与序列化
        extra_kwargs = {
            # 校验规则
            name: {
                # 哪些校验规则
                min_length: 1,
                required: True,
                # 这些校验规则对应的错误消息
                error_messages: {
                    required: 为必填项
                }
            },
            # 只参与反序列化
            publish: {
                write_only: True
            },
            authors: {
                write_only: True
            },
            # 只参与序列化
            img: {
                read_only: True
            },
            # 以下自定义字段可以省略, 默认只参与序列化
            # ‘author_list‘: {
            #     ‘read_only‘: True
            # },
            # ‘publish_name‘: {
            #     ‘read_only‘: True
            # }
        }
        # 群改时需要使用ListSerializer并重写update()方法
        list_serializer_class = V2BookListSerializer

    # 反序列化校验规则
    def validate_name(self, value):
        """校验书名"""
        # 长度
        if len(value) > 15:
            raise ValidationError(不能超过10位)
        return value

    # 全局校验
    def validate(self, attrs):
        """联合校验"""
        # context是视图类传给序列化类的参数
        print(self.context.get(request).method)
        # 校验:同一出版社不能的书名不能重复
        book = Book.objects.filter(name=attrs.get(name), publish=attrs.get(publish), is_delete=False)
        if book:
            # 已存在同名书籍
            raise ValidationError({status: 1, msg: 该出版社已存在同名书籍})
        return attrs

定义一个Meta类, 在Meta下面可以定义如下属性:

1. 设置model = Book, 将序列化类与模型类进行关联

2. 将需要序列化和反序列化的字段都放在fields列表中

  2.1 一些自定的序列化字段如 ‘author_list‘ 和 ‘publish_name‘, 这些字段不是数据库字段, 只是用于序列化给前台更好的展示, 需要在Book的model类中额外添加:

    @property
    def publish_name(self):
        return self.publish.name

    @property
    def author_list(self):
        # mobile=models.F(‘detail__mobile‘)用来将查询的detail__mobile重命名为mobile
        authors = self.authors.values(name, age, mobile=models.F(detail__mobile))
        return authors

3. 设置额外关键字参数extra_kwargs,每个字段都有很多属性可以设置, 在extra_kwargs设置时格式为:

extra_kwargs = {
    ‘字段名1‘: {
        ‘属性名1‘: 属性值1,
        ‘属性名2‘: 属性值2,
         ...
     },
     ‘字段名2‘: {
        ‘属性名1‘: 属性值1,
        ‘属性名2‘: 属性值2,
        ...
    },
    ....
} 

  3.1 read_only(默认为False)只读字段, 只能在序列化时转换为json数据给前台读取, 而在反序列化时不能使用该字段, 因为反序列化需要将前台传入的数据写入数据库中, 如果想设置只能写入, 不能读取, 那么就要设置write_only(默认为False)为True. 这样就能将只参与序列化或反序列化的字段拆开. 同一个字段不能同时设置read_only为True和write_only为True

  3.2 可以设置一些字段的简单验证规则, 如最大长度(max_length), 最小长度(min_length), 是否必输(requierd(默认为False))等

  3.3 error_messages用于将前面定义的简单验证规与自定义的错误消息进行映射

4. 还有一些其他属性如

# fields列表为model中定义的所有字段
#
fields = ‘__all__‘ # 除去下面这些字段, 其他model字段都需要 # exclude = [‘id‘, ‘is_delete‘, ‘create_time‘] # 自动展示深度, 当不展示深度时, 若图书中有出版社信息, 则出版社字段暂时的是其对应的出版商ID, 如果设置了深度为1, 则出版社字段默认展示为所有出版社序列化类中展示的字段, 深度为2以此类推 # depth = 1

5. 在进行批量更新操作时, 需要使用ListSerializer并重写update()方法, 重写update方法的逻辑其实就是遍历model类对象, 再单独调用ModelSerializer的update方法

定义反序列化的验证

反序列化时需要校验request中的data是否有效, 因此在序列化类中可以重写 ‘‘validate_字段名(self, value)‘‘ 和 ‘‘validate(self, attrs)‘‘ 方法, 分别用来做字段的单独校验和字段的联合校验, 如果校验失败, 则抛出ValidationError(‘错误信息xxx‘) 异常

在视图中使用序列化类进行序列化和反序列化

在view中的method中使用序列化类, 这里主要实现10种接口: 单查, 群查, 单增, 群增, 单删, 群删, 单改(整体字段改), 单改(局部字段改), 群改(整体字段改), 群改(局部字段改)

序列化(以get查询为例)

from rest_framework.views import APIView
from rest_framework.response import Response
from books.serializers import BookSerializer
from books import models

class V2BookView(APIView):
    def get(self, request, *args, **kwargs):
        # 获取pk
        pk = kwargs.get(pk)
        try:
            book = models.Book.objects.get(pk=pk, is_delete=False)
        except Exception:
            return MyResponse(status=1, msg=该书不存在)
        # 创建序列化对象, instance参数为获取到的model对象book
        serializer = V2BookSerializer(instance=book)
        # serializer.data返回的是序列化后的json格式数据
        data = {
            status: 0,
            msg: post OK,
            result: serializer.data
        }
        return Response(data=data)

主要步骤为:

1. 查询model对象

2. 使用序列化类通过model对象创建序列化对象

3. 调用序列化对象的data属性获取到序列化后的结果

4. 将结果创建Response对象并返回

反序列化(以post为例)

def post(self, request, *args, **kwargs):
    # 创建反序列化对象, data参数为request.data, request.data能获取到前台提交的常见格式的数据
    serializer = V2BookSerializer(data=request.data)
    # 调用is_valid进行数据校验
    serializer.is_valid(raise_exception=True)
    # 调用save创建model对象并保存
    serializer.save()
    data = {
        status: 0,
        msg: post OK,
        result: serializer.data
    }
    # 返回结果
    return Response(result=serializer.data)

主要步骤为:

1. 获取前台传来的数据, 直接丢给序列化类创建序列化对象

2. 调用序列化对象的is_valid方法进行序列化类中的校验, 若校验失败则会直接抛出异常

3. 若校验成功则调用save()将数据保存至数据库

4. 最后将结果创建Response对象并返回

分析序列化的源码

序列化的继承关系MRO

查看序列化的继承关系MRO可以看到继承顺序, 也是我们看源码的优先顺序

print(BookSerializer.__mro__)
# 打印结果 (
<class books.serializers.BookSerializer>,
<class rest_framework.serializers.ModelSerializer>,
<class rest_framework.serializers.Serializer>,
<class rest_framework.serializers.BaseSerializer>,
<class rest_framework.fields.Field>,
<class object>)

创建序列化类的对象

序列化与反序列化都创建了序列化类的对象, 不同的是序列化传入的参数对为 instance=book , 反序列化传入的参数对为 data=request.data, 源码中根据MRO顺序可以找到序列化基类BaseSerializer的__init__方法中将instance参数和data参数分别存到了不同的属性中

def __init__(self, instance=None, data=empty, **kwargs):
    self.instance = instance
    if data is not empty:
        self.initial_data = data
    self.partial = kwargs.pop(partial, False)
    self._context = kwargs.pop(context, {})
    kwargs.pop(many, None)
    super().__init__(**kwargs)

除了instance和data参数外, 还能接受
  1. many=True, (默认False)用来序列化或反序列化多个对象, 在群增, 群删, 群查, 群改时使用

  2. partial=True, (默认False)用来序列化或反序列化类中fields中定义的部分字段, 本质上是失效掉了其他字段的required=True的属性, 在单改(局部字段), 群改(局部字段)时使用

  3. context={}, (默认None)用来给序列化类传递额外所需的信息, 如request对象等, 实现view和serializer的信息传递

反序列化的验证

序列化类的is_valid用来校验数据是否正确, 根据MRO顺序可以找到源码在BaseSerializer.is_valid中

它接收一个参数raise_exception(默认False), 若设置为True, 则在调用run_validation()校验时如果出错了, 那么就直接继续把异常抛出, 如果设置为False, 则返回bool类型是否校验通过(是否合法)

可以看到具体的校验逻辑还在self.run_validation(中)

def is_valid(self, raise_exception=False):
    代码省略......
    if not hasattr(self, _validated_data):
        try:
            # 运行验证逻辑
            self._validated_data = self.run_validation(self.initial_data)
        except ValidationError as exc:
            self._validated_data = {}
            self._errors = exc.detail
        else:
            self._errors = {}
    # 判断是否直接抛出异常
    if self._errors and raise_exception:
        raise ValidationError(self.errors)
    # 不抛出异常的话返回Bool类型
    return not bool(self._errors)

继续根据MRO顺序可以找到Serializer.run_validation

def run_validation(self, data=empty):
    代码省略......
    # 校验自定义校验中的单个字段
    value = self.to_internal_value(data)
    try:
        # 运行验证器中的验证
        self.run_validators(value)
        # 调用验证方法
        value = self.validate(value)
        assert value is not None, .validate() should return the validated data
    except (ValidationError, DjangoValidationError) as exc:
        raise ValidationError(detail=as_serializer_error(exc))

    return value

1. 首先运行的是 self.to_internal_value(data) , 根据MRO找到serializers.to_internal_value, 可以看到通过反射获取到序列化类中自定的单个字段的验证‘validate_字段名‘的校验方法validate_method, 然后运行该方法

def to_internal_value(self, data):
    代码省略......
    for field in fields:
        validate_method = getattr(self, validate_ + field.field_name, None)
        primitive_value = field.get_value(data)
        try:
            validated_value = field.run_validation(primitive_value)
            if validate_method is not None:
                validated_value = validate_method(validated_value)
        代码省略......
        else:
            set_value(ret, field.source_attrs, validated_value)

    if errors:
        raise ValidationError(errors)

    return ret

2. 获取验证器 self.run_validators(value) , 点入代码可以看到默认的验证器  default_validators = [] 是一个空列表, 如果需要自定义验证器的话需要在序列化类中定义验证器

3. 调用验证方法 self.validate(value) , 这里根据MRO优先找到的就是我们序列化类中自定义的 validate 方法

 save()保存操作

根据MRO找到BaseSerializer.save()方法, 若实例存在, 则调用self.update()更新, 不存在则调用self.create()创建, 继续在MRO中从左往右找update和create方法

def save(self, **kwargs):
    代码省略.....
    # 存在则update
    if self.instance is not None:
        self.instance = self.update(self.instance, validated_data)
        assert self.instance is not None, (
            `update()` did not return an object instance.
        )
    # 不存在则create
    else:
        self.instance = self.create(validated_data)
        assert self.instance is not None, (
            `create()` did not return an object instance.
        )
    return self.instance

发现ModelSerializer中就重写了create()和update()方法,create()中调用了 instance = ModelClass._default_manager.create(**validated_data) 创建model实例对象, update()中遍历验证后的字段, 调用setattr方法设置model对象的属性 setattr(instance, attr, value) , 最后调用 instance.save() 保存修改

请求模块

上一章讲过在RDF真正入口为rest_framework.view的dispatch(), dispatch的第一步就是 request = self.initialize_request(request, *args, **kwargs) 封装请求, 具体代码为

def initialize_request(self, request, *args, **kwargs):
    """
    Returns the initial request object.
    """
    # 获取需要解析的数据
    parser_context = self.get_parser_context(request)

    return Request(
        request,
        parsers=self.get_parsers(),  # 获取解析器, 在调用request.data时会使用到
        authenticators=self.get_authenticators(),  # 获取权限器, 在权限认证时会使用到
        negotiator=self.get_content_negotiator(),
        parser_context=parser_context
    )

创建了一个DRF的Request对象, 查看rest_framework.request.Request类的__init__方法, 可以看到将django原生的request对象封装至了DRF的Request的_request属性中, 在具有了原生request所有的功能的同时, 又添加了一些其他属性, 如解析器, 认证器等等

    def __init__(self, request, parsers=None, authenticators=None,
                 negotiator=None, parser_context=None):
        assert isinstance(request, HttpRequest), (
            The `request` argument must be an instance of 
            `django.http.HttpRequest`, not `{}.{}`.
            .format(request.__class__.__module__, request.__class__.__name__)
        )
        # 将原生request封装至_request属性中
        self._request = request, 
        self.parsers = parsers or ()
        self.authenticators = authenticators or ()
        self.negotiator = negotiator or self._default_negotiator()
        self.parser_context = parser_context
        self._data = Empty
        self._files = Empty
        self._full_data = Empty
        self._content_type = Empty
        self._stream = Empty

        if self.parser_context is None:
            self.parser_context = {}
        self.parser_context[request] = self
        self.parser_context[encoding] = request.encoding or settings.DEFAULT_CHARSET

        force_user = getattr(request, _force_auth_user, None)
        force_token = getattr(request, _force_auth_token, None)
        if force_user is not None or force_token is not None:
            forced_auth = ForcedAuthentication(force_user, force_token)
            self.authenticators = (forced_auth,)

之前原生request获取url参数和form表单提交的参数是分别使用request.GET和request.POST, 现在我们就可分别使用DRF的request.query_params和request.data获取, 并且request.data能获取除form表单格式外的其他常见格式的数据, 如json等等

响应模块

DRF提供了一个Response类来支持HTTP内容响应, 该类是Django中 SimpleTemplateResponse 类的一个子类, 我们并不一定非要使用DRF的 Response 类进行响应,也可以返回常规的 HttpResponse 或 者 StreamingHttpResponse 对象,但是使用 Response 类可以提供一个多种格式的更漂亮 的界面。响应模块的源码在rest_framework.response.py中

class Response(SimpleTemplateResponse):
    """
    An HttpResponse that allows its data to be rendered into
    arbitrary media types.
    """
    def __init__(self, data=None, status=None,
                 template_name=None, headers=None,
                 exception=False, content_type=None):
                 
        super().__init__(None, status=status)

        if isinstance(data, Serializer):
            msg = (
                You passed a Serializer instance as data, but 
                probably meant to pass serialized `.data` or 
                `.error`. representation.
            )
            raise AssertionError(msg)

        self.data = data
        self.template_name = template_name
        self.exception = exception
        self.content_type = content_type

        if headers:
            for name, value in headers.items():
                self[name] = value

查看__init__()方法可以看到其接受的参数如下:

data : 一个字典, 包含想要响应的数据

status : 响应的状态码。默认是200。

template_name : 当选择 HTMLRenderer 渲染器时,指定要使用的模板的名称。

headers : 一个字典,包含响应的HTTP头部信息。

content_type : 响应的内容类型。通常由渲染器自行设置,由协商内容确定,但是在某 些情况下,你需要明确指定内容类型。

自定义响应类

直接调用Response时 return Response(data=serializer.data), 返回的结果只有查询出来的数据, 而一般我们都会加上一些额外的与前台约定的字段, 如status或者error_msg等, 所以我们可以自定义响应类

from rest_framework.response import Response

class MyResponse(Response):
    """继承Response, 封装自己的response"""
    def __init__(self, status=0, msg=ok, http_status=None, headers=None, exception=False, **kwargs):
        # 设置data
        data = {
            status: status,
            msg: msg,
            **kwargs
        }
        # 继承父类
        super().__init__(data=data, status=http_status, template_name=None, headers=headers, exception=exception,
                         content_type=None)

1. 继承rest_framework的Response类

2. 在init方法中添加自定义的参数, 如status, msg等, 其他键值对参数通过**kwargs获取

3. 将自定义的参数和**kwargs(拆包)参数封装到data字典中

4. 调用父类的__init__方法, 将data字典传入

渲染模块(DRF常用模块的配置方法)

在rest_framework的dispatch方法中, 最后调用了 self.response = self.finalize_response(request, response, *args, **kwargs) 生成最终的response, 在 finalize_response 中设置了response的渲染器 response.accepted_renderer = request.accepted_renderer , 渲染器最终是通过 get_renderers 返回的, 其实返回的就是一个个渲染类 renderer_classes 所实例化的对象组成的列表

    def get_renderers(self):
        """
        Instantiates and returns the list of renderers that this view can use.
        """
        return [renderer() for renderer in self.renderer_classes]

DRF的rest_framework.views.py中提供了很多类似 get_renderers 的方法, 如获取解释器 get_parsers , 获取认证器 get_authenticators , 获取权限器 get_permissions 等等

且这些get_xxx方法都是返回一个对应类的列表, 且这些类基本都可以手动全局或局部配置, 若没有手动配置则默认取APIView默认的设置

class APIView(View):
    # The following policies may be set at either globally, or per-view.
    renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
    parser_classes = api_settings.DEFAULT_PARSER_CLASSES
    authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
    throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES
    permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES
    content_negotiation_class = api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASS
    metadata_class = api_settings.DEFAULT_METADATA_CLASS
    versioning_class = api_settings.DEFAULT_VERSIONING_CLASS

这些默认配置都在rest_framework.settings.py中, 默认的配置如下

DEFAULTS = {
    # Base API policies
    DEFAULT_RENDERER_CLASSES: [
        rest_framework.renderers.JSONRenderer,
        rest_framework.renderers.BrowsableAPIRenderer,
    ],
    DEFAULT_PARSER_CLASSES: [
        rest_framework.parsers.JSONParser,
        rest_framework.parsers.FormParser,
        rest_framework.parsers.MultiPartParser
    ],
    DEFAULT_AUTHENTICATION_CLASSES: [
        rest_framework.authentication.SessionAuthentication,
        rest_framework.authentication.BasicAuthentication
    ],
    DEFAULT_PERMISSION_CLASSES: [
        rest_framework.permissions.AllowAny,
    ],
    DEFAULT_THROTTLE_CLASSES: [],
    DEFAULT_CONTENT_NEGOTIATION_CLASS: rest_framework.negotiation.DefaultContentNegotiation,
    DEFAULT_METADATA_CLASS: rest_framework.metadata.SimpleMetadata,
    DEFAULT_VERSIONING_CLASS: None,

    # Generic view behavior
    DEFAULT_PAGINATION_CLASS: None,
    DEFAULT_FILTER_BACKENDS: [],

    # Schema
    DEFAULT_SCHEMA_CLASS: rest_framework.schemas.openapi.AutoSchema,

    # Throttling
    DEFAULT_THROTTLE_RATES: {
        user: None,
        anon: None,
    },
    NUM_PROXIES: None,

    # Pagination
    PAGE_SIZE: None,

    # Filtering
    SEARCH_PARAM: search,
    ORDERING_PARAM: ordering,

    # Versioning
    DEFAULT_VERSION: None,
    ALLOWED_VERSIONS: None,
    VERSION_PARAM: version,

    # Authentication
    UNAUTHENTICATED_USER: django.contrib.auth.models.AnonymousUser,
    UNAUTHENTICATED_TOKEN: None,

    # View configuration
    VIEW_NAME_FUNCTION: rest_framework.views.get_view_name,
    VIEW_DESCRIPTION_FUNCTION: rest_framework.views.get_view_description,

    # Exception handling
    EXCEPTION_HANDLER: rest_framework.views.exception_handler,
    NON_FIELD_ERRORS_KEY: non_field_errors,

    # Testing
    TEST_REQUEST_RENDERER_CLASSES: [
        rest_framework.renderers.MultiPartRenderer,
        rest_framework.renderers.JSONRenderer
    ],
    TEST_REQUEST_DEFAULT_FORMAT: multipart,

    # Hyperlink settings
    URL_FORMAT_OVERRIDE: format,
    FORMAT_SUFFIX_KWARG: format,
    URL_FIELD_NAME: url,

    # Input and output formats
    DATE_FORMAT: ISO_8601,
    DATE_INPUT_FORMATS: [ISO_8601],

    DATETIME_FORMAT: ISO_8601,
    DATETIME_INPUT_FORMATS: [ISO_8601],

    TIME_FORMAT: ISO_8601,
    TIME_INPUT_FORMATS: [ISO_8601],

    # Encoding
    UNICODE_JSON: True,
    COMPACT_JSON: True,
    STRICT_JSON: True,
    COERCE_DECIMAL_TO_STRING: True,
    UPLOADED_FILES_USE_URL: True,

    # Browseable API
    HTML_SELECT_CUTOFF: 1000,
    HTML_SELECT_CUTOFF_TEXT: "More than {count} items...",

    # Schemas
    SCHEMA_COERCE_PATH_PK: True,
    SCHEMA_COERCE_METHOD_NAMES: {
        retrieve: read,
        destroy: delete
    },
}

我们可以在django项目的settings.py中全局配置这些属性类, 如配置渲染器

REST_FRAMEWORK = {
    # 渲染器类
    DEFAULT_RENDERER_CLASSES: [
        rest_framework.renderers.JSONRenderer,  # 渲染json
        rest_framework.renderers.BrowsableAPIRenderer,  # 渲染带有API返回结果的浏览器界面
    ],
    # 异常处理类
    EXCEPTION_HANDLER: utils.exceptions.exception_handler
}

当然我们也可以进行局部配置, 即在具体的视图类中配置, 如

class V2BookView(APIView):
    # 渲染器类
    renderer_classes = [
        rest_framework.renderers.JSONRenderer,  # 渲染json
        rest_framework.renderers.BrowsableAPIRenderer,  # 渲染带有API返回结果的浏览器界面
    ]

    def get(self, request, *args, **kwargs):
        .........

10种接口的简单实现

 在一个视图中可以实现10种接口对图书的增删改查接口, 分别为: 单增, 群增, 单删, 群删, 单局部改, 单整体改, 群局部改, 群整体改, 单查, 群查

路由配置urls.py为:

from django.urls import path
from books import views

urlpatterns = [
    path(v2/, views.V2BookView.as_view()),
    path(v2/<int:pk>/, views.V2BookView.as_view()),
]

视图类views.py为:

class V2BookView(APIView):

    def get(self, request, *args, **kwargs):
        # 获取pk
        pk = kwargs.get(pk)

        if pk:
            # 单查
            try:
                book = models.Book.objects.get(pk=pk, is_delete=False)
            except Exception:
                return MyResponse(status=1, msg=该书不存在)

            serializer = V2BookSerializer(book)
        else:
            # 群查
            books = models.Book.objects.filter(is_delete=False).all()
            serializer = V2BookSerializer(books, many=True)
        return MyResponse(result=serializer.data)

    # 单增: 传的数据是与model对应的字典
    # 群增: 传的是 包含多个model对应字典的 列表或者元组
    def post(self, request, *args, **kwargs):
        if isinstance(request.data, dict):
            # 单增
            serializer = V2BookSerializer(data=request.data)
        elif isinstance(request.data, list):
            # 群增
            serializer = V2BookSerializer(data=request.data, many=True)
        else:
            return MyResponse(status=1, msg=只能为list或列表)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return MyResponse(result=serializer.data)

    # 单删 pk
    # 群删 pks
    def delete(self, request, *args, **kwargs):
        pk = kwargs.get(pk)
        if pk:
            # 单删
            pks = [pk]
        else:
            # 群删
            pks = request.data.get(pks)
        # update返回受影响的行
        if not models.Book.objects.filter(pk__in=pks, is_delete=False).update(is_delete=True):
            return MyResponse(status=1, msg=图书不存在或已被删除)
        return MyResponse()

    # 单整体改 /pk/ dict
    # 群整体改 list
    # 反序列化的目的是: 将众多数据的校验交给序列化类来处理, 让序列化类扮演反序列化角色
    def put(self, request, *args, **kwargs):
        pk = kwargs.get(pk)
        request_data = request.data
        # 将单整体改和群整体改都转化为群整体改
        if pk and isinstance(request_data, dict):
            # 单整体改
            pks = [pk, ]
            request_data = [request_data, ]
        elif not pk and isinstance(request_data, list):
            # 群改, 数据格式: [{"pk":1, "name": "python999"}, {"pk":2, "publish": 4}, {"pk":3, "price": 100}]
            pks = []
            for item in request_data:
                pk = item.get(pk, None)
                if pk:
                    pks.append(pk)
                else:
                    request_data.remove(item)
        else:
            return MyResponse(status=1, msg=格式必须为list或dict)
        # 处理pks, 生成序列化参数instance(books)和data(data_list)
        if pks:
            books = []
            data_list = []
            for index, pk in enumerate(pks):
                try:
                    books.append(models.Book.objects.get(pk=pk, is_delete=False))
                    data_list.append(request_data[index])
                except Exception as e:
                    continue
            if books:
                serializer = V2BookSerializer(instance=books, data=data_list, many=True,
                                              partial=kwargs.get(partial, False), context={"request": request})
                serializer.is_valid(raise_exception=True)
                serializer.save()
                return MyResponse(result=serializer.data)
        return MyResponse(status=1, msg=图书都不存在)

    # 单局部改 /pk/
    # 群局部改
    # 局部改和整体改逻辑一样, 只是在序列化时多了一个partial参数
    def patch(self, request, *args, **kwargs):
        return self.put(request, partial=True, *args, **kwargs)

 

django-rest-framework-源码解析002-序列化/请求模块/响应模块/渲染模块/十大接口

标签:script   empty   查询   func   desc   charset   优先   dump   基类   

原文地址:https://www.cnblogs.com/gcxblogs/p/13272630.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!