• Django REST framework 基本组件


    一、序列化组件

    简单使用

    开发我们的Web API的第一件事是为我们的Web API提供一种将代码片段实例序列化和反序列化为诸如json之类的表示形式的方式。我们可以通过声明与Django forms非常相似的序列化器(serializers)来实现。

    models部分:

    from django.db import models
    
    # Create your models here.
    
    
    class Book(models.Model):
        title=models.CharField(max_length=32)
        price=models.IntegerField()
        pub_date=models.DateField()
        publish=models.ForeignKey("Publish")
        authors=models.ManyToManyField("Author")
        def __str__(self):
            return self.title
    
    class Publish(models.Model):
        name=models.CharField(max_length=32)
        email=models.EmailField()
        def __str__(self):
            return self.name
    
    class Author(models.Model):
        name=models.CharField(max_length=32)
        age=models.IntegerField()
        def __str__(self):
            return self.name
    

    views部分:

    from rest_framework.views import APIView
    from rest_framework.response import Response
    from .models import *
    from django.shortcuts import HttpResponse
    from django.core import serializers
    
    
    from rest_framework import serializers
    
    class BookSerializers(serializers.Serializer):
        title=serializers.CharField(max_length=32)
        price=serializers.IntegerField()
        pub_date=serializers.DateField()
        publish=serializers.CharField(source="publish.name")
        #authors=serializers.CharField(source="authors.all")
        authors=serializers.SerializerMethodField()
        def get_authors(self,obj):
            temp=[]
            for author in obj.authors.all():
                temp.append(author.name)
            return temp
    
    
    class BookViewSet(APIView):
    
        def get(self,request,*args,**kwargs):
            book_list=Book.objects.all()
            # 序列化方式1:
            # from django.forms.models import model_to_dict
            # import json
            # data=[]
            # for obj in book_list:
            #     data.append(model_to_dict(obj))
            # print(data)
            # return HttpResponse("ok")
    
            # 序列化方式2:
            # data=serializers.serialize("json",book_list)
            # return HttpResponse(data)
    
            # 序列化方式3:
            bs=BookSerializers(book_list,many=True)
            return Response(bs.data)
    

    ModelSerializer

    class BookSerializers(serializers.ModelSerializer):
          class Meta:
              model=Book
              fields="__all__"
              depth=1
    

    提交post请求

      def post(self,request,*args,**kwargs):
           
            bs=BookSerializers(data=request.data,many=False)
            if bs.is_valid():
                # print(bs.validated_data)
                bs.save()
                return Response(bs.data)
            else:
                return HttpResponse(bs.errors)
    

    重写save中的create方法

    class BookSerializers(serializers.ModelSerializer):
    
          class Meta:
              model=Book
              fields="__all__"
              # exclude = ['authors',]
              # depth=1
    
          def create(self, validated_data):
            
              authors = validated_data.pop('authors')
              obj = Book.objects.create(**validated_data)
              obj.authors.add(*authors)
              return obj
    

    单条数据的get和put请求

    class BookDetailViewSet(APIView):
    
        def get(self,request,pk):
            book_obj=Book.objects.filter(pk=pk).first()
            bs=BookSerializers(book_obj)
            return Response(bs.data)
    
        def put(self,request,pk):
            book_obj=Book.objects.filter(pk=pk).first()
            bs=BookSerializers(book_obj,data=request.data)
            if bs.is_valid():
                bs.save()
                return Response(bs.data)
            else:
                return HttpResponse(bs.errors)
    

    超链接API:Hyperlinked

    class BookSerializers(serializers.ModelSerializer):
          publish= serializers.HyperlinkedIdentityField(
                         view_name='publish_detail',
                         lookup_field="publish_id",
                         lookup_url_kwarg="pk")
          class Meta:
              model=Book
              fields="__all__"
              #depth=1
    

    urls部分:

    `urlpatterns ``=` `[``    ``url(r``'^books/$'``, views.BookViewSet.as_view(),name``=``"book_list"``),``    ``url(r``'^books/(?P<pk>d+)$'``, views.BookDetailViewSet.as_view(),name``=``"book_detail"``),``    ``url(r``'^publishers/$'``, views.PublishViewSet.as_view(),name``=``"publish_list"``),``    ``url(r``'^publishers/(?P<pk>d+)$'``, views.PublishDetailViewSet.as_view(),name``=``"publish_detail"``),``]`
    

    补充

    class BookSerializer(serializers.ModelSerializer):
        dis_chapter = serializers.SerializerMethodField(read_only=True)
        users = serializers.SerializerMethodField(read_only=True)
        publishers = serializers.SerializerMethodField(read_only=True)
    
        def get_users(self, obj):
            # obj是当前序列化的book对象
            users_query_set = obj.user.all()
            return [{"id": user_obj.pk, "name": user_obj.name} for user_obj in users_query_set]
    
        def get_publishers(self, obj):
            publisher_obj = obj.publisher
            return {"id": publisher_obj.pk, "title": publisher_obj.title}
    
        def get_dis_chapter(self, obj):
            return obj.get_chapter_display()
    
        class Meta:
            model = Book
            # fields = "__all__"
            # 字段是有序的
            fields = ["id", "title","dis_chapter", "pub_time", "publishers", "users","chapter", "user", "publisher"]
            # exclude = ["user"]
            # 分别是所有字段 包含某些字段 排除某些字段
            read_only_fields = ["id", "dis_chapter", "users", "publishers"]
            extra_kwargs = {"title": {"validators": [my_validate,]}, "user": {"write_only": True}, "publisher": {"write_only": True},
                            "chapter": {"write_only": True}}
    
    ModelSerializer
    

    验证

    单个字段的验证

    class BookSerializer(serializers.Serializer):
        id = serializers.IntegerField(read_only=True)
        title = serializers.CharField(max_length=32)
        # 省略了一些字段 跟上面代码里一样的
        # 。。。。。
         def validate_title(self, value):
            if "python" not in value.lower():
                raise serializers.ValidationError("标题必须含有Python")
            return value
    
    单个字段的验证
    

    多个字段的验证

    class BookSerializer(serializers.Serializer):
        id = serializers.IntegerField(read_only=True)
        title = serializers.CharField(max_length=32)
        CHOICES = ((1, "Linux"), (2, "Django"), (3, "Python"))
        chapter = serializers.ChoiceField(choices=CHOICES, source="get_chapter_display", read_only=True)
        w_chapter = serializers.IntegerField(write_only=True)
        pub_time = serializers.DateField()
        date_added = serializers.DateField(write_only=True)
        # 新增了一个上架时间字段  
        # 省略一些字段。。都是在原基础代码上增加的
        # 。。。。。。
    
        # 对多个字段进行验证 要求上架日期不能早于出版日期 上架日期要大
        def validate(self, attrs):
            if attrs["pub_time"] > attrs["date_added"]:
                raise serializers.ValidationError("上架日期不能早于出版日期")
            return attrs
    
    多个字段的验证
    

    验证器

    def my_validate(value):
        if "敏感词汇" in value.lower:
            raise serializers.ValidationError("包含敏感词汇,请重新提交")
        return value
    
    
    class BookSerializer(serializers.Serializer):
        id = serializers.IntegerField(read_only=True)
        title = serializers.CharField(max_length=32, validators=[my_validate])
        # 。。。。。。
        
    
    验证器 validators
    

    二、视图组件之视图三部曲

    使用混合(mixins)

    上一节的视图部分:

    from rest_framework.views import APIView
    from rest_framework.response import Response
    from .models import *
    from django.shortcuts import HttpResponse
    from django.core import serializers
    from rest_framework import serializers
    
    
    class BookSerializers(serializers.ModelSerializer):
          class Meta:
              model=Book
              fields="__all__"
              #depth=1
    
    
    class PublshSerializers(serializers.ModelSerializer):
    
          class Meta:
              model=Publish
              fields="__all__"
              depth=1
    
    
    class BookViewSet(APIView):
    
        def get(self,request,*args,**kwargs):
            book_list=Book.objects.all()
            bs=BookSerializers(book_list,many=True,context={'request': request})
            return Response(bs.data)
    
    
        def post(self,request,*args,**kwargs):
            print(request.data)
            bs=BookSerializers(data=request.data,many=False)
            if bs.is_valid():
                print(bs.validated_data)
                bs.save()
                return Response(bs.data)
            else:
                return HttpResponse(bs.errors)
    
    
    class BookDetailViewSet(APIView):
    
        def get(self,request,pk):
            book_obj=Book.objects.filter(pk=pk).first()
            bs=BookSerializers(book_obj,context={'request': request})
            return Response(bs.data)
    
        def put(self,request,pk):
            book_obj=Book.objects.filter(pk=pk).first()
            bs=BookSerializers(book_obj,data=request.data,context={'request': request})
            if bs.is_valid():
                bs.save()
                return Response(bs.data)
            else:
                return HttpResponse(bs.errors)
    
    
    class PublishViewSet(APIView):
    
        def get(self,request,*args,**kwargs):
            publish_list=Publish.objects.all()
            bs=PublshSerializers(publish_list,many=True,context={'request': request})
            return Response(bs.data)
    
    
        def post(self,request,*args,**kwargs):
    
            bs=PublshSerializers(data=request.data,many=False)
            if bs.is_valid():
                # print(bs.validated_data)
                bs.save()
                return Response(bs.data)
            else:
                return HttpResponse(bs.errors)
    
    
    class PublishDetailViewSet(APIView):
    
        def get(self,request,pk):
    
            publish_obj=Publish.objects.filter(pk=pk).first()
            bs=PublshSerializers(publish_obj,context={'request': request})
            return Response(bs.data)
    
        def put(self,request,pk):
            publish_obj=Publish.objects.filter(pk=pk).first()
            bs=PublshSerializers(publish_obj,data=request.data,context={'request': request})
            if bs.is_valid():
                bs.save()
                return Response(bs.data)
            else:
                return HttpResponse(bs.errors)
    

    mixin类编写视图

    from rest_framework import mixins
    from rest_framework import generics
    
    class BookViewSet(mixins.ListModelMixin,
                      mixins.CreateModelMixin,
                      generics.GenericAPIView):
    
        queryset = Book.objects.all()
        serializer_class = BookSerializers
    
        def get(self, request, *args, **kwargs):
            return self.list(request, *args, **kwargs)
    
        def post(self, request, *args, **kwargs):
            return self.create(request, *args, **kwargs)
    
    
    
    class BookDetailViewSet(mixins.RetrieveModelMixin,
                        mixins.UpdateModelMixin,
                        mixins.DestroyModelMixin,
                        generics.GenericAPIView):
        queryset = Book.objects.all()
        serializer_class = BookSerializers
    
        def get(self, request, *args, **kwargs):
            return self.retrieve(request, *args, **kwargs)
    
        def put(self, request, *args, **kwargs):
            return self.update(request, *args, **kwargs)
    
        def delete(self, request, *args, **kwargs):
            return self.destroy(request, *args, **kwargs)
    

    使用通用的基于类的视图

    通过使用mixin类,我们使用更少的代码重写了这些视图,但我们还可以再进一步。REST框架提供了一组已经混合好(mixed-in)的通用视图,我们可以使用它来简化我们的views.py模块。

    from rest_framework import mixins
    from rest_framework import generics
    
    class BookViewSet(generics.ListCreateAPIView):
    
        queryset = Book.objects.all()
        serializer_class = BookSerializers
    
    class BookDetailViewSet(generics.RetrieveUpdateDestroyAPIView):
        queryset = Book.objects.all()
        serializer_class = BookSerializers
    
    class PublishViewSet(generics.ListCreateAPIView):
    
        queryset = Publish.objects.all()
        serializer_class = PublshSerializers
    
    class PublishDetailViewSet(generics.RetrieveUpdateDestroyAPIView):
        queryset = Publish.objects.all()
        serializer_class = PublshSerializers
    

    viewsets.ModelViewSet

    urls.py:

        url(r'^books/$', views.BookViewSet.as_view({"get":"list","post":"create"}),name="book_list"),
        url(r'^books/(?P<pk>d+)$', views.BookViewSet.as_view({
                    'get': 'retrieve',
                    'put': 'update',
                    'patch': 'partial_update',
                    'delete': 'destroy'
                }),name="book_detail"),
    

    views.py:

    class BookViewSet(viewsets.ModelViewSet):
        queryset = Book.objects.all()
        serializer_class = BookSerializers
    

    路由

    我们上面的路由传参写的特别多~~框架也帮我们封装好了~

    from .views import BookView
    from rest_framework.routers import DefaultRouter
    
    router = DefaultRouter()
    router.register(r"book", BookView)
    
    urlpatterns = [
        # url(r'^book$', BookView.as_view()),
        # url(r'^book/(?P<id>d+)$', BookEditView.as_view()),
        # url(r'^book$', BookView.as_view({"get": "list", "post": "create"})),
        # url(r'^book/(?P<pk>d+)$', BookView.as_view({"get": "retrieve", "patch": "update", "delete": "destroy"})),
    
    ]
    urlpatterns += router.urls
    

    我们可以看到通过框架我们可以把路由视图都变的非常简单

    但是需要自定制的时候还是需要我们自己用APIView写当不需要那么多路由的时候~也不要用这种路由注册

    总之一切按照业务需要去用


    三、认证组件

    局部视图认证

    在app01.service.auth.py:

    class Authentication(BaseAuthentication):
    
        def authenticate(self,request):
            token=request._request.GET.get("token")
            token_obj=UserToken.objects.filter(token=token).first()
            if not token_obj:
                raise exceptions.AuthenticationFailed("验证失败!")
            return (token_obj.user,token_obj)
    

    在views.py:

    def get_random_str(user):
        import hashlib,time
        ctime=str(time.time())
    
        md5=hashlib.md5(bytes(user,encoding="utf8"))
        md5.update(bytes(ctime,encoding="utf8"))
    
        return md5.hexdigest()
    
    
    from app01.service.auth import *
    
    from django.http import JsonResponse
    class LoginViewSet(APIView):
        authentication_classes = [Authentication,]
        def post(self,request,*args,**kwargs):
            res={"code":1000,"msg":None}
            try:
                user=request._request.POST.get("user")
                pwd=request._request.POST.get("pwd")
                user_obj=UserInfo.objects.filter(user=user,pwd=pwd).first()
                print(user,pwd,user_obj)
                if not user_obj:
                    res["code"]=1001
                    res["msg"]="用户名或者密码错误"
                else:
                    token=get_random_str(user)
                    UserToken.objects.update_or_create(user=user_obj,defaults={"token":token})
                    res["token"]=token
    
            except Exception as e:
                res["code"]=1002
                res["msg"]=e
    
            return JsonResponse(res,json_dumps_params={"ensure_ascii":False})
    

    全局视图认证组件

    settings.py配置如下:

    `REST_FRAMEWORK``=``{``    ``"DEFAULT_AUTHENTICATION_CLASSES"``:[``"app01.service.auth.Authentication"``,]``}`
    

    四、权限组件

    局部视图权限

    在app01.service.permissions.py中:

    from rest_framework.permissions import BasePermission
    class SVIPPermission(BasePermission):
        message="SVIP才能访问!"
        def has_permission(self, request, view):
            if request.user.user_type==3:
                return True
            return False
    

    在views.py:

    from app01.service.permissions import *
    
    class BookViewSet(generics.ListCreateAPIView):
        permission_classes = [SVIPPermission,]
        queryset = Book.objects.all()
        serializer_class = BookSerializers
    

    全局视图权限

    settings.py配置如下:

    `REST_FRAMEWORK``=``{``    ``"DEFAULT_AUTHENTICATION_CLASSES"``:[``"app01.service.auth.Authentication"``,],``    ``"DEFAULT_PERMISSION_CLASSES"``:[``"app01.service.permissions.SVIPPermission"``,]``}`
    

    五、throttle(访问频率)组件

    局部视图throttle

    在app01.service.throttles.py中:

    from rest_framework.throttling import BaseThrottle
    
    VISIT_RECORD={}
    class VisitThrottle(BaseThrottle):
    
        def __init__(self):
            self.history=None
    
        def allow_request(self,request,view):
            remote_addr = request.META.get('REMOTE_ADDR')
            print(remote_addr)
            import time
            ctime=time.time()
    
            if remote_addr not in VISIT_RECORD:
                VISIT_RECORD[remote_addr]=[ctime,]
                return True
    
            history=VISIT_RECORD.get(remote_addr)
            self.history=history
    
            while history and history[-1]<ctime-60:
                history.pop()
    
            if len(history)<3:
                history.insert(0,ctime)
                return True
            else:
                return False
    
        def wait(self):
            import time
            ctime=time.time()
            return 60-(ctime-self.history[-1])
    

    在views.py中:

    from app01.service.throttles import *
    
    class BookViewSet(generics.ListCreateAPIView):
        throttle_classes = [VisitThrottle,]
        queryset = Book.objects.all()
        serializer_class = BookSerializers
    

    全局视图throttle

    REST_FRAMEWORK={
        "DEFAULT_AUTHENTICATION_CLASSES":["app01.service.auth.Authentication",],
        "DEFAULT_PERMISSION_CLASSES":["app01.service.permissions.SVIPPermission",],
        "DEFAULT_THROTTLE_CLASSES":["app01.service.throttles.VisitThrottle",]
    }
    

    内置throttle类

    在app01.service.throttles.py修改为:

    class VisitThrottle(SimpleRateThrottle):
    
        scope="visit_rate"
        def get_cache_key(self, request, view):
    
            return self.get_ident(request)
    

    settings.py设置:

    REST_FRAMEWORK={
        "DEFAULT_AUTHENTICATION_CLASSES":["app01.service.auth.Authentication",],
        "DEFAULT_PERMISSION_CLASSES":["app01.service.permissions.SVIPPermission",],
        "DEFAULT_THROTTLE_CLASSES":["app01.service.throttles.VisitThrottle",],
        "DEFAULT_THROTTLE_RATES":{
            "visit_rate":"5/m",
        }
    }
    

    六、解析器

    request类

    django的request类和rest-framework的request类的源码解析

    局部视图

    from rest_framework.parsers import JSONParser,FormParser
    class PublishViewSet(generics.ListCreateAPIView):
        parser_classes = [FormParser,JSONParser]
        queryset = Publish.objects.all()
        serializer_class = PublshSerializers
        def post(self, request, *args, **kwargs):
            print("request.data",request.data)
            return self.create(request, *args, **kwargs)
    

    全局视图

    REST_FRAMEWORK={
        "DEFAULT_AUTHENTICATION_CLASSES":["app01.service.auth.Authentication",],
        "DEFAULT_PERMISSION_CLASSES":["app01.service.permissions.SVIPPermission",],
        "DEFAULT_THROTTLE_CLASSES":["app01.service.throttles.VisitThrottle",],
        "DEFAULT_THROTTLE_RATES":{
            "visit_rate":"5/m",
        },
        "DEFAULT_PARSER_CLASSES":['rest_framework.parsers.FormParser',]
    }
    

    七、分页

    简单分页

    from rest_framework.pagination import PageNumberPagination,LimitOffsetPagination
    
    class PNPagination(PageNumberPagination):
            page_size = 1
            page_query_param = 'page'
            page_size_query_param = "size"
            max_page_size = 5
    
    class BookViewSet(viewsets.ModelViewSet):
    
        queryset = Book.objects.all()
        serializer_class = BookSerializers
        def list(self,request,*args,**kwargs):
    
            book_list=Book.objects.all()
            pp=LimitOffsetPagination()
            pager_books=pp.paginate_queryset(queryset=book_list,request=request,view=self)
            print(pager_books)
            bs=BookSerializers(pager_books,many=True)
    
            #return Response(bs.data)
            return pp.get_paginated_response(bs.data)
    

    偏移分页

    from rest_framework.pagination import LimitOffsetPagination
    

    八、版本控制

    版本控制是做什么用的, 我们为什么要用

    首先我们要知道我们的版本是干嘛用的呢大家都知道我们开发项目是有多个版本的

    随着我们项目的更新版本就越来越多我们不可能新的版本出了以前旧的版本就不进行维护了~~~

    那我们就需要对版本进行控制这个DRF也给我们提供了一些封装好的版本控制方法

    版本控制怎么用

    之前我们学视图的时候知道APIView,也知道APIView返回View中的view函数,然后调用的dispatch方法~

    那我们现在看下dispatch方法看下它都做了什么

    执行self.initial方法之前是各种赋值,包括request的重新封装赋值,下面是路由的分发,那我们看下这个方法都做了什么~~

    我们可以看到,我们的version版本信息赋值给了 request.version 版本控制方案赋值给了 request.versioning_scheme~~

    其实这个版本控制方案就是我们配置的版本控制的类~

    也就是说,APIView通过这个方法初始化自己提供的组件~~

    我们接下来看看框架提供了哪些版本的控制方法~~在rest_framework.versioning里

    框架一共给我们提供了这几个版本控制的方法我们在这里只演示一个因为基本配置都是一样的~~

    详细用法

    我们看下放在URL上携带版本信息怎么配置~~

    1. setting配置

      REST_FRAMEWORK = {
          # 默认使用的版本控制类
          'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning',
          # 允许的版本
          'ALLOWED_VERSIONS': ['v1', 'v2'],
          # 版本使用的参数名称
          'VERSION_PARAM': 'version',
          # 默认使用的版本
          'DEFAULT_VERSION': 'v1',
      }
      
    2. urls.py

      urlpatterns = [
          url(r"^versions", MyView.as_view()),
          url(r"^(?P<version>[v1|v2]+)/test01", TestView.as_view()),
      ]
      
    3. 测试视图

      class TestView(APIView):
          def get(self, request, *args, **kwargs):
              print(request.versioning_scheme)
              ret = request.version
              if ret == "v1":
                  return Response("版本v1的信息")
              elif ret == "v2":
                  return Response("版本v2的信息")
              else:
                  return Response("根本就匹配不到这个路由")
      

      其他的版本控制的类,配置方法都差不多~~这里就不一一例举了
      详细请见:https://www.cnblogs.com/GGGG-XXXX/p/9564651.html



  • 相关阅读:
    PHP trim() 函数
    php 计算2个日期的相差天数
    php date('Y-n-j')的和date('Y-m-d')的区别
    转移服务器
    Invalid argument supplied for foreach()解决办法
    wordpress 后台忘记密码怎么办
    qrcode js插件生成微信二维码
    thinkphp5 注释
    tp5 新增完数据,获取id
    resstFul服务文件上传下载
  • 原文地址:https://www.cnblogs.com/wangyueping/p/11470699.html
Copyright © 2020-2023  润新知