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)
  • 相关阅读:
    有点忙啊
    什么是协程
    HDU 1110 Equipment Box (判断一个大矩形里面能不能放小矩形)
    HDU 1155 Bungee Jumping(物理题,动能公式,弹性势能公式,重力势能公式)
    HDU 1210 Eddy's 洗牌问题(找规律,数学)
    HDU1214 圆桌会议(找规律,数学)
    HDU1215 七夕节(模拟 数学)
    HDU 1216 Assistance Required(暴力打表)
    HDU 1220 Cube(数学,找规律)
    HDU 1221 Rectangle and Circle(判断圆和矩形是不是相交)
  • 原文地址:https://www.cnblogs.com/shijieli/p/14882779.html
Copyright © 2011-2022 走看看