十大序列化接口基础版

十大序列化接口

单查群查接口

主路由urls.py

from django.conf.urls import url, include
from django.contrib import admin
from django.views.static import serve
from django.conf import settings


urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^api/', include('api.urls')),

    url(r'^media/(?P<path>.*)', serve,  {'document_root': settings.MEDIA_ROOT})
]
View Code

子路由urls.py

from . import views
from django.conf.urls import url
urlpatterns = [
    url(r'^books/$', views.BOOKAPIView.as_view()),
    url(r'^books/(?P<pk>\d+)/$', views.BOOKAPIView.as_view()),
]
View Code

二次封装response.py

from rest_framework.response import Response

class APIResponse(Response):

    def __init__(self, status=0, msg='ok', http_status=None,  headers=None, exception=False, **kwargs):
        # 将外界传入的数据状态码,状态信息以及其他所有额外储存在kwargs中的信息,都格式化成data信息
        data = {
            'status': status,  # 数据状态码
            'msg': msg
        }
        if kwargs:
            data.update(kwargs)
        super().__init__(data=data, status=http_status, headers=headers, exception=exception)
View Code

models.py

# 基类:是抽象的(不会完成数据库迁移),目的是提供共有字段的
class BaseModel(models.Model):
    is_delete = models.BooleanField(default=False)
    updated_time = models.DateTimeField(auto_now_add=True)

    class Meta:
        abstract = True  # 必须完成该配置

class Book(BaseModel):
    name = models.CharField(max_length=64)
    price = models.DecimalField(max_digits=5, decimal_places=2, null=True)
    image = models.ImageField(upload_to='img', default='img/default.png')

    publish = models.ForeignKey(to='Publish', related_name='books', db_constraint=False, on_delete=models.DO_NOTHING)
    authors = models.ManyToManyField(to='Author', related_name='books', db_constraint=False)
    
    @property  # @property字段默认就是read_only,且不允许修改
    def publish_name(self):
        return self.publish.name

    @property  # 自定义序列化过程
    def author_list(self):
        temp_author_list = []
        for author in self.authors.all():
            author_dic = {
                "name": author.name
            }
            try:
                author_dic['phone'] = author.detail.phone
            except:
                author_dic['phone'] = ''
            temp_author_list.append(author_dic)
        return temp_author_list
    

class Publish(BaseModel):
    name = models.CharField(max_length=64)


class Author(BaseModel):
    name = models.CharField(max_length=64)


class AuthorDetail(BaseModel):
    phone = models.CharField(max_length=11)
    author = models.OneToOneField(to=Author, related_name='detail', db_constraint=False, on_delete=models.CASCADE)
View Code

serializers.py

from rest_framework import serializers
from . import models

# 群增群改辅助类(了解)
class BookListSerializer(serializers.ListSerializer):
    """
    1)create群增方法不需要重写
    2)update群改方法需要重写,且需要和views中处理request.data的逻辑配套使用
    3)self.child就代表该ListSerializer类绑定的ModelSerializer类
       换句话说,BookListSerializer的self.child就是BookModelSerializer
    """
    # 重新update方法
    def update(self, queryset, validated_data_list):
        return [
            self.child.update(queryset[index], validated_data) for index, validated_data in enumerate(validated_data_list)
        ]

# 主序列化类
class BookModelSerializer(serializers.ModelSerializer):
    class Meta:
        # 配置自定义群增群改序列化类
        list_serializer_class = BookListSerializer

        model = models.Book
        fields = ('name', 'price', 'image', 'publish', 'authors', 'publish_name', 'author_list')
        # fields = ('name', 'price', 'image', 'publish', 'authors', 'abc')
        extra_kwargs = {
            'image': {
                'read_only': True,
            },
            'publish': {  # 系统原有的外键字段,要留给反序列化过程使用,序列化外键内容,用@property自定义
                'write_only': True,
            },
            'authors': {
                'write_only': True,
            },
        }

    # 需求:内外传参
    # 1)在钩子函数中,获得请求请求对象request 视图类 => 序列化类
    # 2)序列化钩子校验过程中,也会产生一些数据,这些数据能不能传递给外界使用:序列化类 => 视图类
    # 序列化类的context属性,被视图类与序列化类共享
    def validate(self, attrs):
        print(self.context)  # 可以获得视图类在初始化序列化对象时传入的context
        # self.context.update({'a': 10})  # 序列化类内部更新context,传递给视图类
        return attrs
View Code

views.py

from rest_framework.views import APIView
from . import models, serializers
from .response import APIResponse

# 六个必备接口:单查、群查、单增、单删、单整体改(了解)、单局部改
# 四个额外接口:群增、群删、群整体改、群局部改
class BookAPIView(APIView):
    # 单查群查
    """
    单查:接口:/books/(pk)/
    群查:接口:/books/
    """
    def get(self, request, *args, **kwargs):
        pk = kwargs.get('pk')
        if pk:
            obj = models.Book.objects.filter(is_delete=False, pk=pk).first()
            serializer = serializers.BookModelSerializer(instance=obj)
            return APIResponse(result=serializer.data)
        else:
            queryset = models.Book.objects.filter(is_delete=False).all()
            serializer = serializers.BookModelSerializer(instance=queryset, many=True)
            return APIResponse(results=serializer.data)

单增群增接口

views.py

"""
单增:接口:/books/   数据:dict
群增:接口:/books/   数据:list
"""
def post(self, request, *args, **kwargs):
    # 如何区别单增群增:request.data是{}还是[]
    if not isinstance(request.data, list):
        # 单增
        serializer = serializers.BookModelSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)  # 如果校验失败,会直接抛异常,返回给前台
        obj = serializer.save()
        # 为什么要将新增的对象重新序列化给前台:序列化与反序列化数据不对等
        return APIResponse(result=serializers.BookModelSerializer(obj).data, http_status=201)
    else:
        # 群增
        serializer = serializers.BookModelSerializer(data=request.data, many=True)
        serializer.is_valid(raise_exception=True)  # 如果校验失败,会直接抛异常,返回给前台
        objs = serializer.save()
        # 为什么要将新增的对象重新序列化给前台:序列化与反序列化数据不对等
        return APIResponse(result=serializers.BookModelSerializer(objs, many=True).data, http_status=201)

单删群删接口

views.py

"""
单删:接口:/books/(pk)/
群删:接口:/books/   数据:[pk1, ..., pkn]
"""
def delete(self, request, *args, **kwargs):
    pk = kwargs.get('pk')
    if pk:
        pks = [pk]  # 将单删伪装成群删一条 
    else:
        pks = request.data  # 群删的数据就是群删的主键们

    try:  # request.data可能提交的是乱七八糟的数据,所以orm操作可能会异常
        rows = models.Book.objects.filter(is_delete=False, pk__in=pks).update(is_delete=True)
    except:
        return APIResponse(1, '数据有误')

    if rows:  # 只要有受影响的行,就代表删除成功
        return APIResponse(0, '删除成功')

    return APIResponse(2, '删除失败')

单整体改群整体改接口

serializers.py

from rest_framework import serializers
from . import models

# 群增群改辅助类(了解)
class BookListSerializer(serializers.ListSerializer):
    """
    1)create群增方法不需要重写
    2)update群改方法需要重写,且需要和views中处理request.data的逻辑配套使用
    3)self.child就代表该ListSerializer类绑定的ModelSerializer类
       换句话说,BookListSerializer的self.child就是BookModelSerializer
    """
    # 重新update方法
    def update(self, queryset, validated_data_list):
        return [
            self.child.update(queryset[index], validated_data) for index, validated_data in enumerate(validated_data_list)
        ]

# 主序列化类
class BookModelSerializer(serializers.ModelSerializer):
    class Meta:
        # 配置自定义群增群改序列化类
        list_serializer_class = BookListSerializer

        model = models.Book
        fields = ('name', 'price', 'image', 'publish', 'authors', 'publish_name', 'author_list')
        # fields = ('name', 'price', 'image', 'publish', 'authors', 'abc')
        extra_kwargs = {
            'image': {
                'read_only': True,
            },
            'publish': {  # 系统原有的外键字段,要留给反序列化过程使用,序列化外键内容,用@property自定义
                'write_only': True,
            },
            'authors': {
                'write_only': True,
            },
        }

    # 需求:内外传参
    # 1)在钩子函数中,获得请求请求对象request 视图类 => 序列化类
    # 2)序列化钩子校验过程中,也会产生一些数据,这些数据能不能传递给外界使用:序列化类 => 视图类
    # 序列化类的context属性,被视图类与序列化类共享
    def validate(self, attrs):
        print(self.context)  # 可以获得视图类在初始化序列化对象时传入的context
        # self.context.update({'a': 10})  # 序列化类内部更新context,传递给视图类
        return attrs

views.py

"""
单整体改:接口:/books/(pk)/ 数据:dict
群整体改:接口:/books/   数据:[{pk1, ...}, ..., {pkn, ...}] | {pks: [pk1, ..., pkn], data: [{}, ..., {}]}
"""
def put(self, request, *args, **kwargs):
    pk = kwargs.get('pk')
    if pk:  #
        try:
            instance = models.Book.objects.get(is_delete=False, pk=pk)
        except:
            return APIResponse(1, 'pk error', http_status=400)
        # 序列化类同时赋值instance和data,代表用data重新更新instance => 修改
        serializer = serializers.BookModelSerializer(instance=instance, data=request.data)
        serializer.is_valid(raise_exception=True)
        obj = serializer.save()
        return APIResponse(result=serializers.BookModelSerializer(obj).data)
    else:  #
        """ 分析request.data数据 [{'pk':1, 'name': '', 'publish': 1, 'authors': [1, 2]}, ...]
        1)从 request.data 中分离出 pks 列表
        2)pks中存放的pk在数据库中没有对应数据,或者对应的数据已经被删除了,这些不合理的pk要被剔除
        3)pks最终转换得到的 objs 列表长度与 request.data 列表长度不一致,就是数据有误
        """
        pks = []
        try:  # 只要不是要求的标准数据,一定会在下方四行代码某一行抛出异常
            for dic in request.data:
                pks.append(dic.pop('pk'))
            objs = models.Book.objects.filter(is_delete=False, pk__in=pks)
            assert len(objs) == len(request.data)  # 两个列表长度必须一致
        except:
            return APIResponse(1, '数据有误', http_status=400)

        serializer = serializers.BookModelSerializer(instance=objs, data=request.data, many=True)
        serializer.is_valid(raise_exception=True)
        objs = serializer.save()
        # 为什么要将新增的对象重新序列化给前台,因为序列化与反序列化数据不对等
        return APIResponse(result=serializers.BookModelSerializer(objs, many=True).data)

单局部改群局部改

serializers.py

from rest_framework import serializers
from . import models

# 群增群改辅助类(了解)
class BookListSerializer(serializers.ListSerializer):
    """
    1)create群增方法不需要重写
    2)update群改方法需要重写,且需要和views中处理request.data的逻辑配套使用
    3)self.child就代表该ListSerializer类绑定的ModelSerializer类
       换句话说,BookListSerializer的self.child就是BookModelSerializer
    """
    # 重新update方法
    def update(self, queryset, validated_data_list):
        return [
            self.child.update(queryset[index], validated_data) for index, validated_data in enumerate(validated_data_list)
        ]

# 主序列化类
class BookModelSerializer(serializers.ModelSerializer):
    class Meta:
        # 配置自定义群增群改序列化类
        list_serializer_class = BookListSerializer

        model = models.Book
        fields = ('name', 'price', 'image', 'publish', 'authors', 'publish_name', 'author_list')
        # fields = ('name', 'price', 'image', 'publish', 'authors', 'abc')
        extra_kwargs = {
            'image': {
                'read_only': True,
            },
            'publish': {  # 系统原有的外键字段,要留给反序列化过程使用,序列化外键内容,用@property自定义
                'write_only': True,
            },
            'authors': {
                'write_only': True,
            },
        }

    # 需求:内外传参
    # 1)在钩子函数中,获得请求请求对象request 视图类 => 序列化类
    # 2)序列化钩子校验过程中,也会产生一些数据,这些数据能不能传递给外界使用:序列化类 => 视图类
    # 序列化类的context属性,被视图类与序列化类共享
    def validate(self, attrs):
        print(self.context)  # 可以获得视图类在初始化序列化对象时传入的context
        # self.context.update({'a': 10})  # 序列化类内部更新context,传递给视图类
        return attrs
View Code

views.py

"""
单局部改:接口:/books/(pk)/ 数据:dict
群局部改:接口:/books/   数据:[{pk1, ...}, ..., {pkn, ...}] | {pks: [pk1, ..., pkn], data: [{}, ..., {}]}
"""
def patch(self, request, *args, **kwargs):
    pk = kwargs.get('pk')
    if pk:  #
        try:
            instance = models.Book.objects.get(is_delete=False, pk=pk)
        except:
            return APIResponse(1, 'pk error', http_status=400)
        # partial=True就是将所有反序列化字段的 required 设置为 False(提供就校验,不提供不校验)
        serializer = serializers.BookModelSerializer(instance=instance, data=request.data, partial=True)
        serializer.is_valid(raise_exception=True)
        obj = serializer.save()
        return APIResponse(result=serializers.BookModelSerializer(obj).data)
    else:  #
        pks = []
        try:  # 只要不是要求的标准数据,一定会在下方三行代码某一行抛出异常
            for dic in request.data:
                pks.append(dic.get('pk'))
            objs = models.Book.objects.filter(is_delete=False, pk__in=pks)
            assert len(objs) == len(request.data)  # 两个列表长度必须一致
        except:
            return APIResponse(1, '数据有误', http_status=400)

        serializer = serializers.BookModelSerializer(
            instance=objs,
            data=request.data,
            many=True,
            partial=True,
            context={'request': request}  # 初始化时,对context赋值,将视图类中数据传递给序列化类
        )

        serializer.is_valid(raise_exception=True)
        objs = serializer.save()

        print(serializer.context)  # 在完成序列化类校验后,可以重新拿到序列化类内部对context做的值更新
        return APIResponse(result=serializers.BookModelSerializer(objs, many=True).data)

十大接口序列化总结

"""
def __init__(self, instance=None, data=empty, **kwargs):
    pass

instance:是要被赋值对象的 - 对象类型数据赋值给instance
data:是要被赋值数据的 - 请求来的数据赋值给data
kwargs:内部有三个属性:many、partial、context
    many:操作的对象或数据,是单个的还是多个的
    partial:在修改需求时使用,可以将所有校验字段required校验规则设置为False
    context:用于视图类和序列化类直接传参使用
"""

# 常见使用
# 单查接口
UserModelSerializer(instance=user_obj)

# 群查接口
UserModelSerializer(instance=user_query, many=True)

# 单增接口,request.data是字典
UserModelSerializer(data=request.data) 

# 群增接口,request.data是列表
UserModelSerializer(data=request.data, many=True)

# 单整体改接口,request.data是字典
UserModelSerializer(instance=user_obj, data=request.data)

# 群整体改接口,request.data是列表,且可以分离出pks,转换成user_queryset
UserModelSerializer(instance=user_queryset, data=request.data, many=True)

# 单局部改接口,request.data是字典
UserModelSerializer(instance=user_obj, data=request.data, partial=True)

# 群局部改接口,request.data是列表,且可以分离出pks,转换成user_queryset
UserModelSerializer(instance=user_queryset, data=request.data, partial=True, many=True)

# 删接口,用不到序列化类

十大接口核心知识小结

"""
1)初始化序列化类,设置partial=True可以将所有反序列化字段的 required 设置为 False(提供就校验,不提供不校验),可以运用在局部修改接口

2)初始化序列化类,设置context={...},在序列化类操作self.context,实现视图类和序列化类数据互通

3)只有要完成资源的群改这种特殊需求时,才需要自定义ListSerializer绑定给自定义的ModelSerializer,重写update方法,来完成需求
"""

 

posted @ 2020-02-21 22:33  Hank·Paul  阅读(474)  评论(0编辑  收藏  举报