Django REST framework 基本組件

1、序列化組件

##簡單使用html

開發咱們的Web API的第一件事是爲咱們的Web API提供一種將代碼片斷實例序列化和反序列化爲諸如json之類的表示形式的方式。咱們能夠經過聲明與Django forms很是類似的序列化器(serializers)來實現。python

models部分:django

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部分:json

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)

##ModelSerializerapp

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方法dom

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:Hyperlinkedide

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

驗證

單個字段的驗證post

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

<br>

2、視圖組件之視圖三部曲

##使用混合(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寫當不須要那麼多路由的時候~也不要用這種路由註冊

總之一切按照業務須要去用

<br>

3、認證組件

##局部視圖認證

在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"``,]``}`

4、權限組件

##局部視圖權限

在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"``,]``}`

5、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",
    }
}

6、解析器

##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',]
}

7、分頁

##簡單分頁

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

8、版本控制

版本控制是作什麼用的, 咱們爲何要用

首先咱們要知道咱們的版本是幹嗎用的呢你們都知道咱們開發項目是有多個版本的

隨着咱們項目的更新~版本就愈來愈多咱們不可能新的版本出了~之前舊的版本就不進行維護了~

那咱們就須要對版本進行控制這個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

<br>

<br>

<br>

相關文章
相關標籤/搜索