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补充:

 

转载于:https://www.cnblogs.com/sima-3/p/11469530.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值