zoukankan      html  css  js  c++  java
  • 组件化10大接口

    组件化10大接口

    表设计

    基表

    Model类的内部配置Meta类要设置abstract=True,这样的Model类就是用来作为基表

    多表:Book,Publish,Author,AuthorDetail

    1. 基表必须设置abstract,基表就是给普通Model类继承使用的,
    2. 设置了abstract就不会完成数据库迁移完成建表
    3. abstract 目的就是只为了继承设置,并且告诉django,不要进行建立这个表格

    表断关联

    db_constraint=False

    1. 表之间没有外键关联,但是有外键逻辑关联(有充当外键的字段)
    2. 断关联后不会影响数据库查询效率,但是会极大提高数据库增删改效率(不影响增删改查操作)
    3. 断关联一定要通过逻辑保证表之间数据的安全
    4. 断关联 db_constraint=False,将表之间的关系断掉,可以在逻辑上依旧可以连表
    5. 级联关系
      • 作者没了,详情也没:on_delete=models.CASCADE,删除作者,详情也会一并删除,级联删除
      • 出版社没了,书还是那个出版社出版:on_delete=models.DO_NOTHING,删除出版社,书的出版社不变,不删除级联
      • 部门没了,员工没有部门(空不能):null=True, on_delete=models.SET_NULL,删除个人的部门,设置外键字段为空
      • 部门没了,员工进入默认部门(默认值):default=0,on_delete=models.SET_DEFAULT。删除个人的部门,设置所有的外键字段为0
      • 对对表不能设计on_delete

    ORM外键设计

    1、一对多:外键放在多的一方
    2、多对多:外键放在常用的一方
    3、一对一:外键放在不常用的一方
    4、外键字段为正向查询字段,related_name是反向查询字段

    author = models.OneToOneField(to='Author', related_name='detail', db_constraint=False, on_delete=models.CASCADE)
    

    有作者可以没有详情,删除作者,详情一定会被级联删除

    外键字段为正向查询字段,related_name是反向查询字段

    related_name='books',反向查询可以直接点books,反向查询的字段
    db_constraint=False,去掉外键建立的关联
    on_delete=models.DO_NOTHING,不执行级联删除

    # ManyToManyField字段不提供设置on_delete,如果想设置关系表级联,只能手动定义关系表
        authors = models.ManyToManyField(to='Author', related_name='books', db_constraint=False)
    

    表设计

    from django.db import models
    from django.conf import settings
    # 一、基表
    # Model类的内部配置Meta类要设置abstract=True,这样的Model类就是用来作为基表
    
    # 多表:Book,Publish,Author,AuthorDetail
    class BaseModel(models.Model):
        is_delete = models.BooleanField(default=False)
        create_time = models.DateTimeField(auto_now_add=True)
    
        class Meta:
            # 基表必须设置abstract,基表就是给普通Model类继承使用的,
            # 设置了abstract就不会完成数据库迁移完成建表
            # abstract 目的就是只为了继承设置,并且告诉django,不要进行建立这个表格
    
            abstract = True  # 抽象的表
    
    
    class Book(BaseModel):# 继承表
        name = models.CharField(max_length=16)
        price = models.DecimalField(max_digits=5, decimal_places=2)
        # related_name='books',反向查询可以直接点books,反向查询的字段
        # db_constraint=False,去掉外键建立的关联
        # on_delete=models.DO_NOTHING,不执行级联删除
        publish = models.ForeignKey(to='Publish', related_name='books', db_constraint=False, on_delete=models.DO_NOTHING)
        # 重点:多对多外键实际在关系表中,ORM默认关系表中两个外键都是级联
        # db_constraint=False
        # ManyToManyField字段不提供设置on_delete,如果想设置关系表级联,只能手动定义关系表
        authors = models.ManyToManyField(to='Author', related_name='books', db_constraint=False)
    
        # 自定义连表深度,不需要反序列化,因为自定义插拔属性不参与反序列化
        @property
        def publish_name(self):
            return self.publish.name
    
        @property
        def author_list(self):
            temp_author_list = []
            for author in self.authors.all():
                temp_author_list.append({
                    'name': author.name,
                    'sex': author.get_sex_display(),
                    'mobile': author.detail.mobile
                })
            return temp_author_list
    
    
    class Publish(BaseModel):
        name = models.CharField(max_length=16)
        address = models.CharField(max_length=64)
    
    
    class Author(BaseModel):
        name = models.CharField(max_length=16)
        sex = models.IntegerField(choices=[(0, '男'), (1, '女')], default=0)
    
    
    class AuthorDetail(BaseModel):
        mobile = models.CharField(max_length=11)
        # 有作者可以没有详情,删除作者,详情一定会被级联删除
        # 外键字段为正向查询字段,related_name是反向查询字段
        author = models.OneToOneField(to='Author', related_name='detail', db_constraint=False, on_delete=models.CASCADE)
    

    响应模块格式化response(二次封装)

    返回信息

    格式:

    {
    	'status':0,
    	'msg':"",
    	'results':'',
    	...
    }
    
    from rest_framework.response import Response
    
    class APIResponse(Response):
        # 格式化data
        def __init__(self, status=0, msg='ok', results=None, http_status=None, headers=None, exception=False, **kwargs):
            data = {  # json的response基础有数据状态码和数据状态信息
                'status': status,
                'msg': msg
            }
            if results is not None:  # 后台有数据,响应数据
                data['results'] = results
            data.update(**kwargs)  # 后台的一切自定义响应数据直接放到响应数据data中
            super().__init__(data=data, status=http_status, headers=headers, exception=exception)
    
    

    自定义异常

    在使用drf的时候,验证与is_valid(raise_exception=True)冲突,因此使用

    from rest_framework.views import exception_handler as drf_exception_handler
    from rest_framework.response import Response
    def exception_handler(exc, context):
        # drf_exception_handler的执行结果就是异常信息的Response对象或None
        # 是Response对象可以直接返回
        # 是None可以从exc中拿到异常信息,从context拿到是谁出现的异常,自己格式化成Response对象返回
        # 重点:自定义异常模块目的是记录异常信息到日志文件 - 产品阶段
        response = drf_exception_handler(exc, context)
        if response is None:
            response = Response({'detail': '%s' % exc}, status=500, exception=True)
        # logging.error(response.data)
        return response
    

    全局配置

    配置settings.py

    REST_FRAMEWORK = {
        # 异常
        # 'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler',#系统异常
        'EXCEPTION_HANDLER': 'api.utils.exception_handler',#重写异常
    
    }
    

    序列化

    # 了解
    fields = '__all__'#返回所有
    exclude = ('id', )#除了id 其他的返回
    
     depth = 1#连表深度查询返回
        
    
    #下面俩个不能一起使用
    fields = '__all__'#返回所有
    exclude = ('id', )#除了id 其他的返回
    
    from rest_framework import serializers
    from . import models
    class BookListSerializer(serializers.ListSerializer):
        # 1、create方法父级ListSerializer已经提供了
        # def create(self, validated_data):
        #     # 通过self.child来访问绑定的ModelSerializer
        #     print(self.child)
        #     raise Exception('我不提供')
    
        # 2、父级ListSerializer没有通过update方法的实现体,需要自己重写
        def update(self, instance, validated_data):
            # print(instance)
            # print(validated_data)
            return [
                self.child.update(instance[i], attrs) for i, attrs in enumerate(validated_data)
            ]
    
    
    
    class BookModelSerializer(serializers.ModelSerializer):
        # 通过BookModelSerializer.Meta.list_serializer_class来访问绑定的ListSerializer
        class Meta:
            # 关联ListSerializer完成群增群改
            #################################################################
            list_serializer_class = BookListSerializer
    
            model = models.Book
            # fields = ('name', 'price', 'publish', 'authors')
            # fields = ('name', 'price', 'publish_name', 'author_list')
            # 了解
            # fields = '__all__'
            # exclude = ('id', )
            # depth = 1
            # 序列化与反序列化整合
            fields = ('name', 'price', 'publish_name',
                      'author_list', 'publish', 'authors')
            extra_kwargs = {
                'publish': {
                    'write_only': True
                },
                'authors': {
                    'write_only': True
                }
            }
    

    子序列化

    子序列化都是提供给外键(正向方向)完成深度查询的,外键数据是唯一:many=False;不唯一:many=True

    只能参与序列化,且反序列化不能写(反序列化外键字段会抛异常)

    # 前提:如果只有查需求的接口,自定义深度还可以用子序列化方式完成
    class PublishModelSerializer(serializers.ModelSerializer):
        # 子序列化都是提供给外键(正向方向)完成深度查询的,外键数据是唯一:many=False;不唯一:many=True
        # 注:只能参与序列化,且反序列化不能写(反序列化外键字段会抛异常)
        books = BookModelSerializer(many=True)
        class Meta:
            model = models.Publish
            fields = ('name', 'address', 'books')
    

    十大接口

    from django.conf.urls import url
    from . import views
    urlpatterns = [
        url(r'^books/$', views.BookAPIView.as_view()),
        url(r'^books/(?P<pk>d+)/$', views.BookAPIView.as_view()),
    ]
    
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from . import models, serializers
    from .response import APIResponse
    class BookAPIView(APIView):
    

    单查、群查

    # 单查、群查
        def get(self, request, *args, **kwargs):
            pk = kwargs.get('pk')
            if pk:
                book_obj = models.Book.objects.filter(pk=pk, is_delete=False).first()
                if not book_obj:
                    return APIResponse(1, 'pk error', http_status=400)
                book_data = serializers.BookModelSerializer(book_obj).data
                return APIResponse(results=book_data)
    
            book_query = models.Book.objects.filter(is_delete=False).all()
            return APIResponse(0, 'ok', data=serializers.BookModelSerializer(book_query, many=True).data)
    

    单删、群删

    # 单删、群删
        def delete(self, request, *args, **kwargs):
            """
            单删:前台数据为pk,接口为 /books/(pk)/
            群删:前台数据为pks,接口为 /books/
            """
            pk = kwargs.get('pk')
            # 将单删群删逻辑整合
            if pk:  # /books/(pk)/的接口就不考虑群删,就固定为单删
                pks = [pk]
            else:
                pks = request.data.get('pks')
            # 前台数据有误(主要是群删没有提供pks)
            if not pks:
                return APIResponse(1, 'delete error', http_status=400)
            # 只要有操作受影响行,就是删除成功,反之失败
            rows = models.Book.objects.filter(is_delete=False, pk__in=pks).update(is_delete=True)
            if rows:
                return APIResponse(0, 'delete ok')
            return APIResponse(1, 'delete failed')
    

    单增、群增

    # 单增、群增
        def post(self, request, *args, **kwargs):
            """
            单增:前台提交字典,接口 /books/
            群增:前台提交列表套字典,接口 /books/
            """
            request_data = request.data
            if isinstance(request_data, dict):  # 单增
                book_ser = serializers.BookModelSerializer(data=request_data)
                if book_ser.is_valid():
                    book_obj = book_ser.save()
                    return APIResponse(results=serializers.BookModelSerializer(book_obj).data)
                return APIResponse(1, msg=book_ser.errors)
            elif isinstance(request_data, list) and len(request_data) != 0 :  # 群增
                book_ser = serializers.BookModelSerializer(data=request_data, many=True)
                book_ser.is_valid(raise_exception=True)
                book_obj_list = book_ser.save()
                return APIResponse(results=serializers.BookModelSerializer(book_obj_list, many=True).data)
            else:
                return APIResponse(1, 'data error', http_status=400)
    

    单整体改、群整体改

    重写update(更新)方法

    父级ListSerializer没有通过update方法的实现体,需要自己重写

    class BookListSerializer(serializers.ListSerializer):
        # 1、create方法父级ListSerializer已经提供了
        # def create(self, validated_data):
        #     # 通过self.child来访问绑定的ModelSerializer
        #     print(self.child)
        #     raise Exception('我不提供')
    
        # 2、父级ListSerializer没有通过update方法的实现体,需要自己重写
        def update(self, instance, validated_data):
            # print(instance)
            # print(validated_data)
            return [
                self.child.update(instance[i], attrs) for i, attrs in enumerate(validated_data)
            ]
    
    
    class BookModelSerializer(serializers.ModelSerializer):
        # 通过BookModelSerializer.Meta.list_serializer_class来访问绑定的ListSerializer
        class Meta:
            # 关联ListSerializer完成群增群改
            list_serializer_class = BookListSerializer
    
    

    单整体改、群整体改

    instance=obj

    # 修改和新增,都需要通过数据,数据依旧给data,修改与新增不同点,instance要被赋值为被修改对象
    book_ser = serializers.BookModelSerializer(instance=book_obj, data=request_data)
    
    
    
    # 单整体改、群整体改
        def put(self, request, *args, **kwargs):
            """
            单整体改:前台提交字典,接口 /books/(pk)/
            群整体改:前台提交列表套字典,接口 /books/,注每一个字典都可以通过pk
            """
            pk = kwargs.get('pk')
            request_data = request.data
            if pk: # 单改
                try:
                    book_obj = models.Book.objects.get(pk=pk)
                except:
                    return APIResponse(1, 'pk error')
    
                # 修改和新增,都需要通过数据,数据依旧给data,修改与新增不同点,instance要被赋值为被修改对象
                book_ser = serializers.BookModelSerializer(instance=book_obj, data=request_data)
                book_ser.is_valid(raise_exception=True)
                book_obj = book_ser.save()
                return APIResponse(results=serializers.BookModelSerializer(book_obj).data)
            else:  # 群改
                if not isinstance(request_data, list) or len(request_data) == 0:
                    return APIResponse(1, 'data error', http_status=400)
    
                # [{pk:1,...}, {pk:3,...}, {pk:100,...}] => [obj1, obj3, obj100] + [{...}, {...}, {...}]
                # 要考虑pk对应的对象是否被删,以及pk没有对应的对象
                # 假设pk3被删,pk100没有 => [obj1] + [{...}]
    
                # 注:一定不要在循环体中对循环对象进行增删(影响对象长度)的操作
                obj_list = []
                data_list = []
                for dic in request_data:
                    # request_data可能是list,单内部不一定是dict
                    try:
                        pk = dic.pop('pk')
                        try:
                            obj = models.Book.objects.get(pk=pk, is_delete=False)
                            obj_list.append(obj)
                            data_list.append(dic)
                        except:
                            pass
                    except:
                        return APIResponse(1, 'data error', http_status=400)
    
                book_ser = serializers.BookModelSerializer(instance=obj_list, data=data_list, many=True)
                book_ser.is_valid(raise_exception=True)
                book_obj_list = book_ser.save()
                return APIResponse(results=serializers.BookModelSerializer(book_obj_list, many=True).data)
    
    

    单局部改、群局部改

    partial=True

    # 局部修改就是在整体修改基础上设置partial=True,将所有参与反序列化字段设置为required=False
    book_ser = serializers.BookModelSerializer(instance=book_obj, data=request_data, partial=True)
    
    
     # 单局部改、群局部改
        def patch(self, request, *args, **kwargs):
            """
            单整体改:前台提交字典,接口 /books/(pk)/
            群整体改:前台提交列表套字典,接口 /books/,注每一个字典都可以通过pk
            """
            pk = kwargs.get('pk')
            request_data = request.data
            if pk:
                try:
                    book_obj = models.Book.objects.get(pk=pk)
                except:
                    return APIResponse(1, 'pk error')
                # 局部修改就是在整体修改基础上设置partial=True,将所有参与反序列化字段设置为required=False
                book_ser = serializers.BookModelSerializer(instance=book_obj, data=request_data, partial=True)
                book_ser.is_valid(raise_exception=True)
                book_obj = book_ser.save()
                return APIResponse(results=serializers.BookModelSerializer(book_obj).data)
    
            else:  # 群改
                if not isinstance(request_data, list) or len(request_data) == 0:
                    return APIResponse(1, 'data error', http_status=400)
    
                # [{pk:1,...}, {pk:3,...}, {pk:100,...}] => [obj1, obj3, obj100] + [{...}, {...}, {...}]
                # 要考虑pk对应的对象是否被删,以及pk没有对应的对象
                # 假设pk3被删,pk100没有 => [obj1] + [{...}]
    
                # 注:一定不要在循环体中对循环对象进行增删(影响对象长度)的操作
                obj_list = []
                data_list = []
                for dic in request_data:
                    # request_data可能是list,单内部不一定是dict
                    try:
                        pk = dic.pop('pk')
                        try:
                            obj = models.Book.objects.get(pk=pk, is_delete=False)
                            obj_list.append(obj)
                            data_list.append(dic)
                        except:
                            pass
                    except:
                        return APIResponse(1, 'data error', http_status=400)
    
                book_ser = serializers.BookModelSerializer(instance=obj_list, data=data_list, many=True, partial=True)
                book_ser.is_valid(raise_exception=True)
                book_obj_list = book_ser.save()
                return APIResponse(results=serializers.BookModelSerializer(book_obj_list, many=True).data)
    
    

    升级版

    单改

    # 单改
        def _single_update(self, pk, data):
            try:
                instance = models.Worker.objects.get(pk=pk, is_delete=False)
                ser = serializers.WorkerModelSerializer(instance=instance, data=data, partial=True)
                ser.is_valid(raise_exception=True)
                obj = ser.save()
                return APIResponse(data=serializers.WorkerModelSerializer(obj).data)
            except:
                return APIResponse(1, 'pk error', http_status=400)
    
    

    群改

     # 群改
        def _many_updata(self, data):
            instance_list = []
            data_list = []
            for dic in data:
                try:
                    # 不是字典,数据错误
                    pk = dic.pop('pk')
                    if len(dic) == 0: raise Exception('随便')
                    try:
                        # pk没有,对应的obj没有,对应的obj已删除,该数据丢弃
                        obj = models.Worker.objects.get(pk=pk, is_delete=False)
                        instance_list.append(obj)
                        data_list.append(dic)
                    except:
                        pass
                except:
                    return APIResponse(1, 'data error', http_status=400)
    
            ser = serializers.WorkerModelSerializer(instance=instance_list, data=data_list, partial=True, many=True)
            ser.is_valid(raise_exception=True)
            obj_list = ser.save()
            return APIResponse(data=serializers.WorkerModelSerializer(obj_list, many=True).data)
    
    
    
        def patch(self, request, *args, **kwargs):
            pk = kwargs.get('pk')
            request_data = request.data
            if pk and isinstance(request_data, dict) and len(request_data) != 0:
                return self._single_update(pk, request_data)
            elif not pk and isinstance(request_data, list) and len(request_data) != 0:
                return self._many_updata(request_data)
            else:
                return APIResponse(1, 'data error', http_status=400)
    
    
  • 相关阅读:
    博客园添加访问人数统计【转】
    Android环境下通过C框架层控制WIFI【转】
    用户态文件系统fuse学习【转】
    linux内核 RCU机制详解【转】
    使用diff制作补丁【学习笔记】
    OAuth2授权原理
    Code! MVC 5 App with Facebook, Twitter, LinkedIn and Google OAuth2 Sign-on (C#)
    lock关键字只不过是C#提供的语法糖
    关于OATUH中的AUTHRAZITON CODE和TOKEN的关系,实际上就是这么回事
    SQL Server 索引设计指南
  • 原文地址:https://www.cnblogs.com/SkyOceanchen/p/11908991.html
Copyright © 2011-2022 走看看