zoukankan      html  css  js  c++  java
  • DRF(四)

    一.自定义反序列化字段

    # 一些只参与反序列化的字段,但是不与数据库关联
    # 在序列化类中规定,并在校验字段时从校验的参数字典中剔除 (比如re_password)
    class PublishModelSerializer(serializers.ModelSerializer):
        # 自定义不入库的 反序列化 字段
        re_name = serializers.CharField(write_only=True)
        class Meta:
            model = models.Publish
            fields = ('name', 're_name', 'address')
        def validate(self, attrs):
            name = attrs.get('name')
            re_name = attrs.pop('re_name')  # 剔除
            if name != re_name:
                raise serializers.ValidationError({'re_name': '确认名字有误'})
            return attrs  

    二.模型类中自定义序列化深度

    # model类中自定义插拔的外键序列化字段,可以采用外键关联表的序列化类来完成深度查询
    class Book(BaseModel):
        # ...
        @property
        def publish_detail(self):
            from . import serializers
            data = serializers.PublishModelSerializer(self.publish).data
            return data

    三.接口操作总结( 单查/群查/单增/群增/单删/群删/单局部改/单全局改/群局部改/群全局改)

           a.路由层 url.py(app中)

    from django.conf.urls import url
    from . import views
    urlpatterns = [
          # ...
        url(r'^v3/books/$', views.BookV3APIView.as_view()),
        url(r'^v3/books/(?P<pk>.*)/$', views.BookV3APIView.as_view()),
    ]

           b. 模型层 models.py

    # 修改部分(群改):取消book类 name 与 publish 联合唯一
    from django.db import models
    from utils.model import BaseModel
    class Book(BaseModel):
        name = models.CharField(max_length=64)
        price = models.DecimalField(max_digits=5, decimal_places=2)
        img = models.ImageField(upload_to='img', default='img/default.png')
        publish = models.ForeignKey(to='Publish', null=True,
                                    related_name='books',
                                    db_constraint=False,
                                    on_delete=models.DO_NOTHING,
                                    )
        authors = models.ManyToManyField(to='Author', null=True,
                                         related_name='books',
                                         db_constraint=False,
                                         )
    
        @property
        def publish_name(self):
            return self.publish.name
    
        @property
        def authors_info(self):
            author_list = []
            for author in self.authors.all():
                author_list.append({
                    'name': author.name,
                    'age': author.age,
                    'mobile': author.detail.mobile
                })
            return author_list
    
        @property
        def publish_bac(self):
            from . import serializers
            data = serializers.PublishModelSerializer(self.publish).data
            return data
    
        class Meta:
            db_table = 'old_boy_book'
            verbose_name = '书籍'
            verbose_name_plural = verbose_name
            # 联合唯一
            # unique_together = ('name', 'publish')
    
        def __str__(self):
            return self.name
    
    
    class Publish(BaseModel):
        name = models.CharField(max_length=64)
        address = models.CharField(max_length=64)
    
        class Meta:
            db_table = 'old_boy_publish'
            verbose_name = '出版社'
            verbose_name_plural = verbose_name
    
        def __str__(self):
            return self.name
    
    
    class Author(BaseModel):
        name = models.CharField(max_length=64)
        age = models.IntegerField()
    
        @property
        def mobile(self):
            return self.detail.mobile
    
        class Meta:
            db_table = 'old_boy_author'
            verbose_name = '作者'
            verbose_name_plural = verbose_name
    
        def __str__(self):
            return self.name
    
    
    class AuthorDetail(BaseModel):
        mobile = models.CharField(max_length=11)
    
        author = models.OneToOneField(to='Author', null=True,
                                      related_name='detail',
                                      db_constraint=False,
                                      on_delete=models.CASCADE
                                      )
    
        class Meta:
            db_table = 'old_boy_author_detail'
            verbose_name = '作者详情'
            verbose_name_plural = verbose_name
    
        def __str__(self):
            return '%s的详情' % self.author.name

         c.序列化层:api/serializers.py

    群增与群改反序列化实现
    1)ModelSerializer默认不通过群改功能,需要在Meta中设置 list_serializer_class
    2)自定义ListSerializer子类,重写update方法,将子类绑定给 list_serializer_class
    3)重写update方法中通过 代表要更新的对象们instance 及 提供的更新数据们validated_data
           得到 更新后的对象们instance 返回
    class BookV3ListSerializer(serializers.ListSerializer):
        def update(self, instance, validated_data):
            '''
            :param instance: [book_obj1, ..., book_obj2]
            :param validated_data: [{更新数据的字段}, ..., {更新数据的字段}]
            :return: [book_new_obj1, ..., book_new_obj2]
            '''
            for index, obj in enumerate(instance):  # type: int, models.Book
                # 单个对象数据更新 - 一个个属性更新值
                for attr, value in validated_data[index].items():
                    # 对象有更新数据字典的key对应的属性,才完成该属性的值更新
                    if hasattr(obj, attr):
                        setattr(obj, attr, value)
                # 信息同步到数据库
                obj.save()
            return instance
    
    class BookV3ModelSerializer(serializers.ModelSerializer):
        class Meta:
            model = models.Book
            fields = ('name', 'price', 'publish', 'authors', 'img', 'publish_name', 'authors_info')
            list_serializer_class = BookV3ListSerializer
            extra_kwargs = {
                'publish': {
                    'required': True,
                    'write_only': True
                },
                'authors': {
                    'required': True,
                    'write_only': True
                },
                'img': {
                    'read_only': True
                }
            }
        def validate_name(self, value):
            if 'sb' in value:
                raise serializers.ValidationError('书名有敏感词汇')
            return value
        def validate(self, attrs):
            name = attrs.get('name')
            publish = attrs.get('publish')
            if models.Book.objects.filter(name=name, publish=publish):
                raise serializers.ValidationError({'book': '书籍以存在'})
            return attrs

    补充:受群增启发自定义群改

    补充自定义update方法

    源码修改:

    d.视图层:api/views.py

    (1)单增群增: 把单增群增数据都统一转化成[{},...]的形式,序列化时都要加上 many = True;

    (2)单改群改:首先预处理需要更新的data及其pks,将其统一改成列表的形式,然后需要从数据库中取出与之pk对应的未删除的book_obj,剔除

    data及book_obj中不存在或者已删除的数据,append到列表中,完成反序列化修改数据,many=True,单改时patial=True,群改时model模型

    中联合唯一会导致出错,需要取消

    (3)单删群删:将pk统一改为列表的形式(群删传输pks就以列表的形式),视图函数中取到queryset对象,通过update方法将is__delete=False

    改为True
    class BookV3APIView(APIView):
        # 单查群查
        def get(self, request, *args, **kwargs):
            pk = kwargs.get('pk')
            if pk:
                book_obj = models.Book.objects.filter(is_delete=False, pk=pk).first()
                if not book_obj:
                    return APIResponse(1, 'pk error')
                book_ser = serializers.BookV3ModelSerializer(book_obj)
                return APIResponse(0, 'ok', results=book_ser.data)
            book_query = models.Book.objects.filter(is_delete=False).all().order_by('-id')
            book_ser = serializers.BookV3ModelSerializer(book_query, many=True)
            return APIResponse(0, 'ok', results=book_ser.data)
        
        
        # 单增群增: 把单增群增数据都统一转化成[{},...]的形式,序列化时都要加上 many = True
        def post(self, request, *args, **kwargs):
            # 单增 /books/1/  data  {}
            # 群增 /books/    data  [{}, ..., {}]
            request_data = request.data
            if isinstance(request_data, dict):
                data = [request_data, ]
            elif isinstance(request_data, list):
                data = request_data
            else:
                return APIResponse(1, '数据格式有误')
            book_ser = serializers.BookV3ModelSerializer(data=data, many=True)
            if book_ser.is_valid():
                book_obj_list = book_ser.save()
                return APIResponse(0, 'ok',
                    results=serializers.BookV3ModelSerializer(book_obj_list, many=True).data
                )
            else:
                return APIResponse(1, '添加失败', results=book_ser.errors)
    
            
        """
        1)先确定要更新的对象 主键们 与 数据们
        2)通过校验数据库剔除 已删除的对象 与 不存在的对象
            主键们 => 剔除过程 => 可以修改的对象们
            数据们 => 剔除过程 => 可以修改的对象们对应的数据们
        3)反序列化及校验过程
            通过 => save() 完成更新
            失败 => ser_obj.errors 返回
        """
        # 单改群改
        def patch(self, request, *args, **kwargs):
            # 单改 /books/(pk)/  data  {"name": "new_name", ...}
            # 群改 /books/  data  [{"pk": 1, "name": "new_name", ...}, ...,{"pk": n, "name": "new_name", ...}]
            # 结果:
            # pks = [1, ..., n] => book_query => instance
            # data = [{"name": "new_name", ...}, ..., {"name": "new_name", ...}] => data
    
            # 数据预处理
            pk = kwargs.get('pk')
            request_data = request.data
            if pk:
                if not isinstance(request_data, dict):
                    return APIResponse(1, '单增数据有误')
                pks = [pk, ]
                data = [request_data, ]
            elif isinstance(request_data, list):
                try:
                    pks = []
                    for dic in request_data:
                        # pks.append(dic.pop('pk'))
                        pks.append(dic.get('pk'))
                    data = request_data
                except:
                    return APIResponse(1, '群增数据有误')
            else:
                return APIResponse(1, '数据格式有误')
    
            # 将 已删除的书籍 与 不存在的书籍 剔除 (提供修改的数据相对应也剔除)
            book_query = []
            filter_data = []
            for index, pk in enumerate(pks):
                book_obj = models.Book.objects.filter(is_delete=False, pk=pk).first()
                if book_obj:
                    book_query.append(book_obj)
                    filter_data.append(data[index])
            # 反序列化完成数据的修改
            book_ser = serializers.BookV3ModelSerializer(instance=book_query, data=filter_data, many=True, partial=True)
            if book_ser.is_valid():
                book_obj_list = book_ser.save()
                return APIResponse(1, 'ok',
                    results=serializers.BookV3ModelSerializer(book_obj_list, many=True).data
                )
            else:
                return APIResponse(1, '更新失败', results=book_ser.errors)
    
        # 单删群删  
        def delete(self, request, *args, **kwargs):
            # 单删 /books/(pk)/
            # 群删 /books/  数据包携带 pks => request.data
            pk = kwargs.get('pk')
            if pk:
                pks = [pk]
            else:
                pks = request.data.get('pks')
            if not pks:
                return APIResponse(1, '删除失败')
            book_query = models.Book.objects.filter(is_delete=False, pk__in=pks)
            if not book_query.update(is_delete=True):  # 操作的记录大于0就记录删除成功
                return APIResponse(1, '删除失败')
            return APIResponse(0, '删除成功')

    四.视图家族

    views:基本视图
    APIView

    generics:工具视图
    GenericAPIView 该家族的基类
               将 queryset 和 serializer_class 封装成类属性,提供了三个方法

    class BookGenericAPIView(generics.GenericAPIView):
        queryset = models.Book.objects.filter(is_delete=False).order_by('-id')
        serializer_class = serializers.BookModelSerializer

              get_queryset() | get_object() | get_serializer(*args, **kwargs)

    GenericAPIView众多子类
              继承 (mixins中若干功能类, GenericAPIView基类)
              提供 get | post | put | patch | delete 五大接口
              eg:RetrieveUpdateAPIView就可以完成 单取(get),单局部(patch)及整体(put)修改

    mixins:视图工具集
             五大工具类:
                     RetrieveModelMixin:retrieve 单取
                     ListModelMixin:list 群取
                    CreateModelMixin:create 单增
                    UpdateModelMixin:update 单整体改 partial_update 单局部改
                    DestroyModelMixin:destroy 单删


    viewsets:视图集
                       ViewSetMixin:视图集工具 - 重写as_view - 将 请求方式 映射到视图类中的 指定方法
                      .as_view({'get': 'retrieve', 'delete': 'remove_obj'})
    GenericViewSet:

                       与模型类有关的接口视图集 - 可以从mixins那继承功能,也可以自定义功能
    ViewSet:

                       与模型类无关或不是标准模型类接口 - 一般都是自定义功能

    开发中常用类:
                     views:APIView
                     generics:ListAPIView, ...
                     viewsets:GenericViewSet
    a.路由

    from django.conf.urls import url
    from . import views
    urlpatterns = [
        # views
        url(r'^books/$', views.BookAPIView.as_view()),
        url(r'^books/(?P<pk>.*)/$', views.BookAPIView.as_view()),
    
        # generics
        url(r'^v1/books/$', views.BookGenericAPIView.as_view()),
        url(r'^v1/books/(?P<pk>.*)/$', views.BookGenericAPIView.as_view()),
        
        # mixins + generics
        url(r'^v2/books/$', views.BookMixinsGenericAPIView.as_view()),
        url(r'^v2/books/(?P<pk>.*)/$', views.BookMixinsGenericAPIView.as_view()),
        
        # 系统整合mixins、generics
        url(r'^v3/books/$', views.BookRetrieveUpdateAPIView.as_view()),
        url(r'^v3/books/(?P<pk>.*)/$', views.BookRetrieveUpdateAPIView.as_view()),
    
        # viewsets
        url(r'^v4/books/$', views.BookGenericViewSet.as_view({
            'get': 'list',
            'post': 'create'
        })),
        url(r'^v4/books/(?P<pk>.*)/$', views.BookGenericViewSet.as_view({
            'get': 'retrieve',
            'put': 'update',
            'patch': 'partial_update',
            'delete': 'remove_book'
        })),
    ]
    b.序列化层:api/serializers.py
    from rest_framework import serializers
    from . import models
    class BookModelSerializer(serializers.ModelSerializer):
        class Meta:
            model = models.Book
            fields = ('name', 'price', 'publish', 'authors')
            extra_kwargs = {
                'publish': {
                    # 数据库有默认值,必须反序列化的字段,要设置required=True
                    'required': True,  
                    'write_only': True
                },
                'authors': {
                    'required': True,
                    'write_only': True
                }
            }
    c.视图层:api/views.py
    from rest_framework import views, generics, mixins, viewsets
    from . import models, serializers
    from utils.response import APIResponse
    
    
    # 基础
    class BookAPIView(views.APIView):
        def get(self, request, *args, **kwargs):
            book_query = models.Book.objects.filter(is_delete=False).order_by('-id').all()
            book_ser = serializers.BookModelSerializer(book_query, many=True)
            return APIResponse(0, 'ok', results=book_ser.data)
    
        def post(self, request, *args, **kwargs):
            book_ser = serializers.BookModelSerializer(data=request.data)
            book_ser.is_valid(raise_exception=True)
            book_obj = book_ser.save()
            return APIResponse(0, 'ok', results=serializers.BookModelSerializer(book_obj).data)
    
    # v1 generics - 视图基类
    class BookGenericAPIView(generics.GenericAPIView):
        queryset = models.Book.objects.filter(is_delete=False).order_by('-id')
        serializer_class = serializers.BookModelSerializer
    
        def get(self, request, *args, **kwargs):
            if 'pk' in kwargs:
                book_obj = self.get_object()
                book_ser = self.get_serializer(book_obj)
                return APIResponse(0, 'ok', results=book_ser.data)
            book_query = self.get_queryset()
            book_ser = self.get_serializer(book_query, many=True)
            return APIResponse(0, 'ok', results=book_ser.data)
    
        def post(self, request, *args, **kwargs):
            book_ser = self.get_serializer(data=request.data)
            book_ser.is_valid(raise_exception=True)
            book_obj = book_ser.save()
            return APIResponse(0, 'ok', results=self.get_serializer(book_obj).data)
    
    # v2 mixins - 工具集
    class BookMixinsGenericAPIView(mixins.RetrieveModelMixin, mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView):
        queryset = models.Book.objects.filter(is_delete=False).order_by('-id')
        serializer_class = serializers.BookModelSerializer
        def get(self, request, *args, **kwargs):
            if 'pk' in kwargs:  #注:主键pk在Mixin类中可以直接获取
                response = self.retrieve(request, *args, **kwargs)
            else:
                response = self.list(request, *args, **kwargs)
            return APIResponse(0, 'ok', results=response.data)
    
        def post(self, request, *args, **kwargs):
            response = self.create(request, *args, **kwargs)
            return APIResponse(0, 'ok', results=response.data)
    
    # v3 视图基类子类 - 工具视图类
    class BookRetrieveUpdateAPIView(generics.RetrieveUpdateAPIView):
        queryset = models.Book.objects.filter(is_delete=False).order_by('-id')
        serializer_class = serializers.BookModelSerializer
    
    
    # v4 视图集 - 快速实现模型类五大接口 - 自定义删除方法
    class BookGenericViewSet(mixins.RetrieveModelMixin, mixins.ListModelMixin, mixins.CreateModelMixin, mixins.UpdateModelMixin, viewsets.GenericViewSet):
        queryset = models.Book.objects.filter(is_delete=False).order_by('-id')
        serializer_class = serializers.BookModelSerializer
    
        def remove_book(self, request, *args, **kwargs):
            pk = kwargs.get('pk')
            try:
                book_obj = models.Book.objects.get(is_delete=False, pk=pk)
                book_obj.is_delete = True
                book_obj.save()
                return APIResponse(0, '删除成功')
            except:
                return APIResponse(1, '删除失败')

     深层authdetail数据未知显示bug补充:

  • 相关阅读:
    Microsoft Enterprise Library 5.0 系列(二) Cryptography Application Block (初级)
    Microsoft Enterprise Library 5.0 系列(五) Data Access Application Block
    Microsoft Enterprise Library 5.0 系列(八) Unity Dependency Injection and Interception
    Microsoft Enterprise Library 5.0 系列(九) Policy Injection Application Block
    Microsoft Enterprise Library 5.0 系列(三) Validation Application Block (高级)
    软件研发打油诗祝大家节日快乐
    从挖井的故事中想到开发管理中最容易忽视的几个简单道理
    ITIL管理思想的执行工具发布
    管理类软件设计“渔”之演化
    20070926日下午工作流与ITILQQ群 事件管理 讨论聊天记录
  • 原文地址:https://www.cnblogs.com/sima-3/p/11469530.html
Copyright © 2011-2022 走看看