zoukankan      html  css  js  c++  java
  • DRF 基本功能梳理 demo

    模型类

    from django.db import models
    
    
    # Create your models here.
    
    class Book(models.Model):
        title = models.CharField(max_length=50, verbose_name="标题", help_text="标题")  # help_text : 文档的显示信息
        collect_num = models.IntegerField(default=0, verbose_name="收藏数", help_text="收藏数")
        author = models.ForeignKey('Author', on_delete=models.CASCADE, related_name='books', help_text="作者")
    
        def __str__(self):
            return self.title
    
    
    class Author(models.Model):
        name = models.CharField(max_length=30, verbose_name="姓名", help_text="姓名")
        age = models.IntegerField(default=18, verbose_name="年龄", help_text="年龄")
        publish = models.ManyToManyField('Publish', help_text="出版社")
    
        def __str__(self):
            return self.name
    
    
    class Publish(models.Model):
        name = models.CharField(max_length=30, verbose_name="出版社名", help_text="出版社名")
        create_dt = models.DateField(verbose_name="成立时间", default="1995-06-01", help_text="成立时间")
    
        def __str__(self):
            return self.name

    序列化类

    from rest_framework import serializers
    from .models import Book, Author, Publish
    
    
    
    # 自定义校验方法
    def check_name(value):
        if "羊驼" not in value:
            raise serializers.ValidationError("必须要有羊驼")
    
        return value
    
    
    class AuthorSerializer(serializers.ModelSerializer):
        # 常规方式对字段进行校验, 将 model 的字段在这里重复写一遍
        name = serializers.CharField(max_length=30, label="姓名", validators=[check_name])  # validators 自定义校验方法
       """Meta 相关属性"""
        class Meta:
            model = Author
            # fields = ["id", "name"] # 自己指定要序列化的字段
            """
            fields 和 exclude 不能同时存在
            Cannot set both 'fields' and 'exclude' options on serializer AuthorSerializer.
            这两个字段的源码位置 ModelSerializer.get_field_names
            
            # exclude = ["id"]  # 排除某些字段
            """
            # 生成指定字段
            fields = "__all__"  # 所有的字段都加进来
            # 设置只读字段
            read_only_fields = []  # 源码位置 ModelSerializer.get_extra_kwargs
            # 给字段添加额外约束
            extra_kwargs = {  # # 源码位置 ModelSerializer.get_extra_kwargs
                "name": {
                    "max_length": 30,
                    "min_length": 1,
                }
    
            }
    
        """多对一的复写"""
        # 多对一的地方需要使用反向字段 如果未设置则为 关联对象表名+ _set 如果设置了 related_name, 则直接使用 related_name
        # 多的一方无法自定义字段国扩展
        # books = serializers.PrimaryKeyRelatedField(read_only=True, many=True)
        # 使用 关联对象的  __str__ 方法作为填充
        books = serializers.StringRelatedField(read_only=True, many=True)
    
    
    class BookSerializer(serializers.ModelSerializer):
        class Meta:
            model = Book
            fields = "__all__"
    
        """一对多字段的复写, 若不复写取出的是 id (int) """""
        # 0. 默认的外键字段序列化方式, 取出的是 关联对象的 ID/ PK
        # author = serializers.PrimaryKeyRelatedField(read_only=True)
        # 1. source 对序列化方式进行复写
        author_id = serializers.CharField(source="author.id", read_only=True)  # str(obj.publish.id) 取 pk 也是一样
        # 2. 可实现自定义拓展字段, 一对多 在 "一" 的一方可以使用, 在 "多" 的一方不可使用
        author_name = serializers.CharField(source="author.name", read_only=True)
        # 3. 返回 关联对象的 __str__ 方法 - 必须基于已有的字段
        # author = serializers.StringRelatedField(read_only=True)
        # 4. 使用序列化器作为关联 - 必须基于已有的字段
        author = AuthorSerializer(read_only=True)  # 取出所有的子字段
    
        """
        单字段校验 - 实现对字段的额外要求的校验
        固定格式: validate(可自动提示) + _ + 校验字段名
        自定义逻辑在主校验逻辑之后, 相当于额外校验, 而非完全替换校验
        """""
    
        @staticmethod
        def validate_title(value):
            """自定义逻辑"""
            if "羊驼" not in value:
                # return False # return 不管是什么都是通过
                # 必须抛出异常才可以表示校验不通过
                """
                HTTP 400 Bad Request
                Allow: GET, POST, HEAD, OPTIONS
                Content-Type: application/json
                Vary: Accept
    
                {
                    "title": [
                        "不赞美羊驼不通过"
                    ]
                }
                """
                raise serializers.ValidationError("不赞美羊驼不通过")
    
            return value  # 通过返回 value 即可
    
        """
        多字段校验 - 实现对字段的额外要求的校验
        validate
        自定义逻辑在主校验逻辑之后, 相当于额外校验, 而非完全替换校验
        """""
    
        def validate(self, attrs):
            """
            bs = BookSerializer(data=request.data)
            attrs: 就是外界穿过来的数据  request.data
            
            多字段的校验里面也可以对单字段分别校验
            """""
            # 自定义逻辑
            title = attrs["title"]
            collect_num = attrs["collect_num"]
            if str(collect_num) not in title:
                """
                HTTP 400 Bad Request
                Allow: GET, POST, HEAD, OPTIONS
                Content-Type: application/json
                Vary: Accept
                
                {
                    "non_field_errors": [
                        "书名里面要有收藏数里面的数字"
                    ]
                }
                """
                raise serializers.ValidationError("书名里面要有收藏数里面的数字")
    
            return attrs  # 校验通过返回原参数
    
        """
        数据入库 - 创建 create
        数据入库 - 更新 update
        
        """
    
        def create(self, validated_data):
            """
    
            :param validated_data:  校验后的数据
            :return:
            """
            book = Book.objects.create(**validated_data)
    
            return book
    
        def update(self, instance, validated_data):
            """
    
            :param instance: 待更新的对象
            :param validated_data: 校验后的数据
            :return:
            """
            instance.title = validated_data["title"]
            instance.collect_num = validated_data["collect_num"]
            instance.save()
            return instance
    
    
    class PublishSerializer(serializers.ModelSerializer):
        class Meta:
            model = Publish
            fields = "__all__"

    路由

    主路由

    """untitled1 URL Configuration
    
    The `urlpatterns` list routes URLs to views. For more information please see:
        https://docs.djangoproject.com/en/2.2/topics/http/urls/
    Examples:
    Function views
        1. Add an import:  from my_app import views
        2. Add a URL to urlpatterns:  path('', views.home, name='home')
    Class-based views
        1. Add an import:  from other_app.views import Home
        2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
    Including another URLconf
        1. Import the include() function: from django.urls import include, path
        2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
    """
    from django.contrib import admin
    from django.urls import path, include
    from rest_framework.documentation import include_docs_urls
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('yangtuo/', include('yangtuo.urls')),
        path('docs/', include_docs_urls(title='yangtuo API')),
    ]

    子路由

    from rest_framework import routers
    from .views import BooksView, AuthorsView, PublishesView
    from django.urls import path
    
    routers = routers.DefaultRouter()
    routers.register('books', viewset=BooksView)
    routers.register('author', viewset=AuthorsView)
    routers.register('publish', viewset=PublishesView)
    #action 装饰器替换
    """
    # 自定义路由
    path("publish/get_yangtuo_publish/", PublishesView.as_view({"get": "get_yangtuo_publish"})),
    # 自定义部分更新路由
    path("publish/change_publish/<int:pk>/", PublishesView.as_view({"put": "change_publish"}))
    """
    urlpatterns = [
    
    ]
    urlpatterns += routers.urls
    print(urlpatterns)

    视图类

    from django_filters.rest_framework import DjangoFilterBackend
    from rest_framework.authentication import SessionAuthentication, BasicAuthentication
    from rest_framework.filters import OrderingFilter
    from rest_framework.pagination import LimitOffsetPagination, PageNumberPagination
    from rest_framework.permissions import IsAuthenticated
    from rest_framework.throttling import UserRateThrottle
    from rest_framework.views import exception_handler
    
    from rest_framework.views import APIView
    from rest_framework.viewsets import ModelViewSet
    from rest_framework.response import Response
    from rest_framework.decorators import action
    
    from .models import Book, Author, Publish
    
    from .serializers import BookSerializer, AuthorSerializer, PublishSerializer
    
    
    # 序列化相关的注释解析
    class BooksView(ModelViewSet):
        queryset = Book.objects.all()
        serializer_class = BookSerializer
    
        def list(self, request, *args, **kwargs):
            books = Book.objects.all()
            # 多个对象的时候 要 many=True
            bs = BookSerializer(instance=books, many=True)  # instance 要被序列化的对象
            return Response(bs.data)
    
        def create(self, request, *args, **kwargs):
            """序列化器对数据类型的校验
            1. 字段类型 int/char/data/bool/mail/uuid....
            2. 字段属性 max_length/required/read_only
            3. 单字段 (方法)
            4. 多字段 (方法)
            5. 自定义 (方法)
            """""
            print(request.data)
            """
            {
                "csrfmiddlewaretoken": "o1XJ6weot4dx4fXwgIC2goYW3aeHUXer4IsIVoL7oyw31gfAi7TTUsTjnxBn9X0F",
                "title": "海洋之歌",
                "collect_num": "999"
            }
            """
            bs = BookSerializer(data=request.data)  # 把数据序列化 用 data 参数传递
            if bs.is_valid(raise_exception=True):  # True / False
                # raise_exception 控制是否提示校验不通过的信息报错
                """ 报错回传示例
                HTTP 400 Bad Request
                Allow: GET, POST, HEAD, OPTIONS
                Content-Type: application/json
                Vary: Accept
                
                {
                    "title": [
                        "该字段不能为空。"
                    ],
                    "collect_num": [
                        "请填写合法的整数值。"
                    ]
                }
                """
                bs.save()  # 调用序列化器里面执行 create 方法
                return Response(bs.data)
    
        def update(self, request, *args, **kwargs):
            book = Book.objects.get(id=kwargs["pk"])
            # instance 要更新的对象, data 更新的数据
            bs = BookSerializer(instance=book, data=request.data)
            if bs.is_valid(raise_exception=True):  # 校验
                bs.save()  # 入库
                return Response(bs.data)
    
    # 视图类的相关注解
    # 0.视图类继承关系
    """
    from rest_framework.viewsets import ModelViewSet
    ModelViewSet(mixins.CreateModelMixin,
                 mixins.RetrieveModelMixin,
                 mixins.UpdateModelMixin,
                 mixins.DestroyModelMixin,
                 mixins.ListModelMixin,
                 GenericViewSet)
    
    from rest_framework.viewsets import ReadOnlyModelViewSet
    ReadOnlyModelViewSet(mixins.RetrieveModelMixin,
                         mixins.ListModelMixin,
                         GenericViewSet)             
    
    from rest_framework.generics import CreateAPIView.....
    CreateAPIView(mixins.CreateModelMixin, GenericAPIView)
    ListAPIView(mixins.ListModelMixin, GenericAPIView)
    RetrieveAPIView(mixins.RetrieveModelMixin, GenericAPIView)
    .....
    
    
                      
        from rest_framework import mixins
        mixins
            CreateModelMixin - >    create()
            CreateModelMixin - >    list()
            RetrieveModelMixin - >    retrieve()
            UpdateModelMixin - >    update()
            DestroyModelMixin - >    destroy()
            
        from rest_framework.viewsets import GenericViewSet        
        GenericViewSet(ViewSetMixin, generics.GenericAPIView):                   
    
            ViewSetMixin.as_view()       
                        self.request = request
                        self.args = args
                        self.kwargs = kwargs
                        return self.dispatch(request, *args, **kwargs)
            
            GenericAPIView.(views.APIView)
                        get_queryset()
                        get_object()
                        get_serializer()
                        filter_queryset()
                        paginate_queryset()
                        
                APIView(View):
                    as_view()   
                    check_permissions()
                    ...
                
                
    """
    # 1. request 的参数获取
    """
    request.GET - > request.query_params
    request.POST / body - > request.data
    """
    # 2. 视图返回封装
    """
    HttpResponse/JsonResponse..... -> 
        Response(data=None, status=None, template_name=None, headers=None, exception=False, content_type=None)
    """
    # 3. 返回状态码封装
    """
    from rest_framework import status
    status.HTTP_100_CONTINUE....
    """
    # 4. 方法封装
    """
    get - > list / retrieve
    post - > create
    put - > update
    delete - > destroy
    """
    # 5. 常用属性行为对象封装
    """
    GenericAPIView: 
        queryset = None 
        serializer_class = None
        lookup_field = 'pk' # 默认查询字段
        lookup_url_kwarg = None
        
        
        # 使用示例
        # queryset = Author.objects.all()
        # serializer_class = AuthorSerializer
        
        filter_backends = api_settings.DEFAULT_FILTER_BACKENDS # 过滤器
        pagination_class = api_settings.DEFAULT_PAGINATION_CLASS # 分页器
        
        get_queryset()
        get_serializer()
        get_object() # 基于 lookup_field  / lookup_url_kwarg
    """
    # 6. CURD 封装
    """
    ModelViewSet(mixins.CreateModelMixin,
                 mixins.RetrieveModelMixin,
                 mixins.UpdateModelMixin,
                 mixins.DestroyModelMixin,
                 mixins.ListModelMixin,
                 GenericViewSet)
    """
    
    
    class AuthorsView(ModelViewSet):
        queryset = Author.objects.all()  # 可以被使用 self.get_queryset() 的地方调用到
        serializer_class = AuthorSerializer  # 可以被使用 self.get_serializer() 的地方调用到
        lookup_url_kwarg = "id"  # 优先级比 lookup_field 高, 源码: lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field
        # 默认就是 pk
        lookup_field = "pk"  # lookup_url_kwarg / lookup_field 设定的值必须是数据库有的字段
    
        def list(self, request, *args, **kwargs):
            # 动态查询
            """
            不要使用 
            authors = self.queryset 
            动态查询是没法自动拿到 .all() 的, 因此调用的时候还需要自己再加上 .all()
            要不这样 
            authors = self.queryset.all()
            或者这样
            authors = self.get_queryset()
            """""
            authors = self.get_queryset()  # self.queryset
            authors_serializer = self.get_serializer()  # self.serializer_class
    
        def retrieve(self, request, *args, **kwargs):
            # author = self.queryset.get(kwargs["id"])
            author = self.get_object()  # 等价上面
    
    
    # 自定义分页器
    class MyPageNumberPagination(PageNumberPagination):
        page_size = 1000  # 复写全局的页面大小
        page_size_query_param = 'page_size'  # 前段指定每页大小的定义字段
        max_page_size = 10000  # 每页最大可以指定的数量, 为了防止前段穿过来 page_size=100000000000000这样的情况
    
    
    # 自定义异常处理
    def my_exception_handler(exc, context):
        response = exception_handler(exc, context)
    
        if response is not None:
            # APIException 类的异常
            response.data['status_code'] = response.status_code
        # 其他异常类型自定义处理
        else:
            from django.db import DatabaseError
            if isinstance(exc, DatabaseError):
                return Response("数据库我特么裂开")
            else:
                return Response("其他异常")
    
        return Response("假设这里是个很好看的 404页面")
    
    
    # 自定义方法/权限/认证/频率/分页/过滤/排序/异常处理/文档 相关注释
    class PublishesView(ModelViewSet):
        """ # 文档里面的注释信息
        list:
            获取所有出版社
        """
        queryset = Publish.objects.all()
        serializer_class = PublishSerializer
        """局部认证是完全替换全局, 而非取并集, 即两种同时配置, 局部生效, 全局失效"""
        # 局部认证
        authentication_classes = [SessionAuthentication, BasicAuthentication]
        # 局部权限
        permission_classes = [IsAuthenticated]
        # 局部限流
        throttle_classes = [UserRateThrottle]
        # throttle_scope = "yangtuo"    # 限流器使用 ScopedRateThrottle 时的映射字段
        # 局部分页器
        pagination_class = MyPageNumberPagination  # 可使用自定义分页器, 复写 PageNumberPagination 的属性实现自定义字段生效
        # 过滤器 搜索 / 排序
        filter_backends = [DjangoFilterBackend,
                           OrderingFilter]  # 注意导入的时候  from rest_framework.filters import OrderingFilter
        filterset_fields = ['name', 'create_dt', 'id']
        ordering_fields = ['id', 'create_dt']  # ordering 前段参数 倒叙加个 -
    
        # 自定义接口方法, 以及生成路由
        @action(methods=['GET'], detail=False)  # detail 表示是否有参数
        def get_yangtuo_publish(self, request):
            publishes = Publish.objects.filter(name__contains="羊驼")
            publish_serializer = self.serializer_class(instance=publishes, many=True)
            return Response(publish_serializer.data)
    
        # 额外添加参数, 更新局部信息
        @action(methods=['PUT'], detail=True)
        def change_publish(self, request, pk):
            publish = self.get_object()
            data = request.data
            # partial 参数携带表示只对部分数据进行校验保存
            publish_serializer = self.serializer_class(instance=publish, data=data, partial=True)
            if publish_serializer.is_valid(raise_exception=True):
                publish_serializer.save()
                return Response(publish_serializer.data)
  • 相关阅读:
    iOS类似QQ好友展开和合并列表的实现代码
    UIPickerView常见属性、常见方法(包括代理方法和数据源方法)的一些说明
    xcode 最近打开文件列表显示为空或不显示最近打开的项目或(no recent projects)解决办法
    KSImageNamed-Xcode插件在xcode 6.4/6.3或其他版本中不能使用解决方案
    喜欢的一句话
    progressdialog的setProgress没反应
    android gradle升级(手动|速度快些)
    详解java继承
    (转)详解Android中AsyncTask的使用
    android selector的item属性
  • 原文地址:https://www.cnblogs.com/shijieli/p/14882779.html
Copyright © 2011-2022 走看看