Django REST framework 1

Django REST framework

Django REST framework官方文檔:點擊  中文文檔:點擊前端

  1. 安裝djangorestframework:pip3 install djangorestframework (pip3 list 查看詳情和版本信息)
  2. 註冊rest_framework(settings.py)
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'app01',
    'rest_framework',
]
settings.py

DRF序列化

作先後端分離的項目,先後端交互通常都選擇JSON數據格式,JSON是一個輕量級的數據交互格式。後端給前端數據的時候都要轉成json格式,那就須要對從數據庫拿到的數據進行序列化。python

要用DRF的序列化,就要遵循人家框架的一些標準git

  1. 聲明序列化類(app01目錄下新建serializers.py文件)
  2. Django CBV繼承類是View,如今DRF要用APIView
  3. Django中返回的時候用HTTPResponse,JsonResponse,render ,DRF用Response
  4. DRF反序列化

    • 當前端給後端發post的請求的時候,前端給後端傳過來的數據,要進行一些校驗而後保存到數據庫。
    • 這些校驗以及保存工做,DRF的Serializer也提供了一些方法了,
    • 首先要寫反序列化用的一些字段,有些字段要跟序列化區分開,
    • Serializer提供了.is_valid()和.save()方法

1、Serializer

一、聲明序列化類(app01中serializers.py)

from rest_framework import serializers
from .models import Book

class PublisherSerializer(serializers.Serializer):
    id = serializers.IntegerField()
    title = serializers.CharField(max_length=32)


class AuthorSerializer(serializers.Serializer):
    id = serializers.IntegerField()
    name = serializers.CharField(max_length=32)


book_obj = {
        "title": "Alex的使用教程",
        "w_category": 1,
        "pub_time": "2018-10-09",
        "publisher_id": 1,
        "author_list": [1, 2]
    }


data = {
    "title": "Alex的使用教程2"
}

#3.1 驗證器 validators  定義函數寫驗證規則
def my_validate(value):
    if "敏感信息" in value.lower():
        raise serializers.ValidationError("不能含有敏感信息")
    else:
        return value


class BookSerializer(serializers.Serializer):
    id = serializers.IntegerField(required=False)  #required=False  不須要校驗
    title = serializers.CharField(max_length=32, validators=[my_validate])  #3.2 驗證器  validators用自定義的驗證方法,比validate_title權重高
    CHOICES = ((1, "Python"), (2, "Go"), (3, "Linux"))
    category = serializers.ChoiceField(choices=CHOICES, source="get_category_display", read_only=True)  #choices
    w_category = serializers.ChoiceField(choices=CHOICES, write_only=True)
    pub_time = serializers.DateField()
    #外鍵關係的序列化
    publisher = PublisherSerializer(read_only=True)  #read_only=True只序列化的時候用
    publisher_id = serializers.IntegerField(write_only=True)  #ForeignKey   #write_only=True只反序列化的時候用
    #ManyToMany的序列化
    author = AuthorSerializer(many=True, read_only=True)   #ManyToManyField
    author_list = serializers.ListField(write_only=True)
    #新建
    def create(self, validated_data):
        book = Book.objects.create(title=validated_data["title"], category=validated_data["w_category"],
                                pub_time=validated_data["pub_time"], publisher_id=validated_data["publisher_id"])
        book.author.add(*validated_data["author_list"])
        return book
    #修改
    def update(self, instance, validated_data):
        instance.title = validated_data.get("title", instance.title)
        instance.category = validated_data.get("w_category", instance.w_category)
        instance.pub_time = validated_data.get("pub_time", instance.pub_time)
        instance.publisher_id = validated_data.get("publisher_id", instance.publisher_id)
        if validated_data.get("author_list"):
            instance.author.set(validated_data["author_list"])
        instance.save()
        return instance
    #驗證
    #一、單個字段的驗證
    def validate_title(self, value):
        if "python" not in value.lower():
            raise serializers.ValidationError("標題必須含有python")
        return value
    #二、多個字段的驗證
    def validate(self, attrs):
        if attrs["w_category"] == 1 and attrs["publisher_id"] == 1:
            return attrs
        else:
            raise serializers.ValidationError("分類以及標題不符合要求")
    #三、驗證器 validators
app01中serializers.py

二、用聲明的序列化類去序列化(app01中views.py)

from .models import Book

from rest_framework.views import APIView
from rest_framework.viewsets import GenericViewSet
from rest_framework.response import Response
from .serializers import BookSerializer



class BookView(APIView):
    def get(self, request):
        # book_obj = Book.objects.first()
        # ret = BookSerializer(book_obj)
        book_list = Book.objects.all()
        ret = BookSerializer(book_list, many=True)  #多個ManyToManyField
        return Response(ret.data)


    def post(self, request):
        print(request.data)
        serializer = BookSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        else:
            return Response(serializer.errors)

class BookEditView(APIView):
    def get(self, request, id):
        book_obj = Book.objects.filter(id=id).first()
        ret = BookSerializer(book_obj)
        return Response(ret.data)

    def put(self, request, id):
        book_obj = Book.objects.filter(id=id).first()
        serializer = BookSerializer(book_obj, data=request.data, partial=True) #partial=True容許部分進行更新
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        else:
            return Response(serializer.errors)

    def delete(self, request, id):
        book_obj = Book.objects.filter(id=id).first()
        book_obj.delete()
        return Response("")
app01中views.py

2、ModelSerializer

  • 跟模型緊密相關的序列化器
  • 根據模型自動生成一組字段
  • 默認實現了.update()以及.create()方法

一、定義一個ModelSerializer序列化器(app01中serializers.py)

from rest_framework import serializers
from .models import Book

# 驗證器 validators  定義函數寫驗證規則
def my_validate(value):
    if "敏感信息" in value.lower():
        raise serializers.ValidationError("不能含有敏感信息")
    else:
        return value


class BookSerializer(serializers.ModelSerializer):
    #category = serializers.CharField(source="get_category_display",read_only=True)   #自定製
    category_display = serializers.SerializerMethodField(read_only=True)  # SerializerMethodField
    publisher_info = serializers.SerializerMethodField(read_only=True)
    authors = serializers.SerializerMethodField(read_only=True)

    def get_category_display(self, obj):
        return obj.get_category_display()

    def get_authors(self, obj):
        # obj是當前序列化的book對象
        #外鍵關聯的對象有不少字段是用不到的~都傳給前端會有數據冗餘~就須要去定製序列化外鍵對象的哪些字段~~
        authors_query_set = obj.author.all()
        return [{"id": author_obj.id, "name": author_obj.name} for author_obj in authors_query_set]

    def get_publisher_info(self, obj):
        # obj 是咱們序列化的每一個Book對象
        # 外鍵關聯的對象有不少字段是用不到的~都傳給前端會有數據冗餘~就須要去定製序列化外鍵對象的哪些字段~~
        publisher_obj = obj.publisher
        return {"id": publisher_obj.id, "title": publisher_obj.title}

    class Meta:
        model = Book
        # fields = ["id", "title", "pub_time"]
        # exclude = ["user"]
        # 包含某些字段 排除某些字段
        fields = "__all__"
        # depth = 1  #depth 表明找嵌套關係的第1層
        #注意:當序列化類MATE中定義了depth時,這個序列化類中引用字段(外鍵)則自動變爲只讀
        #read_only_fields = ["id", "category_display", "publisher_info", "authors"]
        extra_kwargs = {"category": {"write_only": True}, "publisher": {"write_only": True},
                        "author": {"write_only": True},"title": {"validators": [my_validate,]}}
app01中serializers.py
from rest_framework import serializers
from api import models


class CourseModelSerializer(serializers.ModelSerializer):
    # price = serializers.SerializerMethodField()
    # learn_num = serializers.SerializerMethodField()
    learn_num = serializers.IntegerField(source='order_details.count')
    course_detail_id = serializers.IntegerField(source='coursedetail.id')

    # def get_price(self, obj):
    #     # 把全部課程永久有效的價格拿出來
    #     price_obj = obj.price_policy.all().filter(valid_period=999).first()
    #     return price_obj.price

    # def get_learn_num(self, obj):
    #     return obj.order_details.count()

    # 修改序列化結果的終極方法
    def to_representation(self, instance):
        # 調用父類的同名方法把序列化的結果拿到
        data = super().to_representation(instance)
        # 針對序列化的結果作一些自定製操做
        # 判斷當前這個課程是否有永久有效的價格
        price_obj = instance.price_policy.all().filter(valid_period=999).first()
        if price_obj:
            # 有永久有效的價格
            data['has_price'] = True
            data['price'] = price_obj.price
        else:
            # 沒有永久有效的價格策略
            data['has_price'] = False
        return data

    class Meta:
        model = models.Course
        fields = '__all__'


class CourseCategoryModelSerializer(serializers.ModelSerializer):

    class Meta:
        model = models.CourseCategory
        fields = '__all__'
to_representation

3、Serializer和ModelSerializer的區別

4、JsonResponse和Django序列化

#app01中models.py
from django.db import models
__all__ = ["Book", "Publisher", "Author"]


class Book(models.Model):
    title = models.CharField(max_length=32, verbose_name="圖書名稱")
    CHOICES = ((1, "Python"), (2, "Go"), (3, "Linux"))
    category = models.IntegerField(choices=CHOICES, verbose_name="圖書的類別")
    pub_time = models.DateField(verbose_name="圖書的出版日期")

    publisher = models.ForeignKey(to="Publisher", on_delete=None)
    author = models.ManyToManyField(to="Author")

    def __str__(self):
        return self.title

    class Meta:
        verbose_name_plural = "01-圖書表"
        db_table = verbose_name_plural


class Publisher(models.Model):
    title = models.CharField(max_length=32, verbose_name="出版社的名稱")

    def __str__(self):
        return self.title

    class Meta:
        verbose_name_plural = "02-出版社表"
        db_table = verbose_name_plural


class Author(models.Model):
    name = models.CharField(max_length=32, verbose_name="做者的姓名")

    def __str__(self):
        return self.name

    class Meta:
        verbose_name_plural = "03-做者表"
        db_table = verbose_name_plural

#DRFDemo中urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
    path('admin/', admin.site.urls),
    path('books/', include("SerDemo.urls")),
]
#app01中urls.py
from django.urls import path, include
from .views import BookView, BookEditView
urlpatterns = [
    path('list', BookView.as_view()),
    path('retrieve/<int:id>', BookEditView.as_view()),
]
app01中models.py 和 app01中的urls.py
#app01/views.py
from django.views import View
from django.http import HttpResponse, JsonResponse
from django.core import serializers
from .models import Book, Publisher

class BookView(View):
    #初版 用.values JsonResponse實現序列化
    def get(self, request):
        book_list = Book.objects.values("id", "title", "category", "pub_time", "publisher")
        book_list = list(book_list)
        # 若是須要取外鍵關聯的字段信息 須要循環獲取外鍵 再去數據庫查而後拼接成想要的
        ret = []
        for book in book_list:
            publisher_id = book["publisher"]
            publisher_obj = Publisher.objects.filter(id=publisher_id).first()
            book["publisher"] = {
                "id": publisher_id,
                "title": publisher_obj.title
            }
            ret.append(book)
        # ret = json.dumps(book_list, ensure_ascii=False)
        # return HttpResponse(ret) #時間
        return JsonResponse(ret, safe=False, json_dumps_params={"ensure_ascii": False})

    #第二版 用django serializers實現序列化
    # 可以獲得要的效果,可是結構有點複雜,並且choices不能顯示對應的文本
    def get(self, request):
        book_list = Book.objects.all()
        ret = serializers.serialize("json", book_list, ensure_ascii=False)
        return HttpResponse(ret)
JsonResponse和Django序列化

DRF的視圖

  • 在Django REST Framework中內置的Request類擴展了Django中的Request類,實現了不少方便的功能--如請求數據解析和認證等。
  • 好比,區別於Django中的request從request.GET中獲取URL參數,從request.POST中取某些狀況下的POST數據。
  • 在APIView中封裝的request,就實現了請求數據的解析:
  • 對於GET請求的參數經過request.query_params來獲取。
  • 對於POST請求、PUT請求的數據經過request.data來獲取。

1、源碼查找

  1. django中寫CBV的時候繼承的是View,rest_framework繼承的是APIView
  2. 不論是View仍是APIView最開始調用的都是as_view()方法。
  3. APIView繼承了View, 而且執行了View中的as_view()方法,最後用csrf_exempt()方法包裹view把view返回了,用csrf_exempt()方法包裹後去掉了csrf的認證。
  4. 在View中的as_view方法返回了view函數,而view函數執行了self.dispatch()方法,可是這裏是APIView調用的,因此先從APIView找dispatch()方法。
  5. APIView的dispatch()方法中給request從新賦值了
  6. 去initialize_request中看下把什麼賦值給了request,而且賦值給了self.request, 也就是視圖中用的request.xxx究竟是什麼
  7. 能夠看到,這個方法返回的是Request這個類的實例對象,注意看下這個Request類中的第一個參數request,是走django的時候的原來的request
  8. 去Request這個類裏看 這個Request類把原來的request賦值給了self._request, 也就是說之後_request是老的request,新的request是這個Request類
  9. request.query_params 存放的是get請求的參數 request.data 存放的是全部的數據,包括post請求的以及put,patch請求
  10. 相比原來的django的request,如今的request更加精簡,清晰了
  11. 框架提供了一個路由傳參的方法ViewSetMixin
#1.徒手壘代碼階段
class SchoolView(APIView):
    def get(self, request, *args, **kwargs):
        query_set = models.School.objects.all()
        ser_obj = app01_serializers.SchoolSerializer(query_set, many=True)
        return Response(ser_obj.data)


class SchoolDetail(APIView):
    def get(self, request, pk, *args, **kwargs):
        obj = models.School.objects.filter(pk=pk).first()
        ser_obj = app01_serializers.SchoolSerializer(obj)
        return Response(ser_obj.data)

#路由
url(r'school/$', views.SchoolView.as_view()),
url(r'school/(?P<pk>\d+)/$', views.SchoolDetail.as_view()),

#2.使用混合類階段

class SchoolView(GenericAPIView, mixins.ListModelMixin):
    queryset = models.School.objects.all()
    serializer_class = app01_serializers.SchoolSerializer

    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)


class SchoolDetail(GenericAPIView, mixins.RetrieveModelMixin, mixins.CreateModelMixin):
    queryset = models.School.objects.all()
    serializer_class = app01_serializers.SchoolSerializer

    def get(self, request, pk, *args, **kwargs):
        return self.retrieve(request, pk, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)

#路由
url(r'school/$', views.SchoolView.as_view()),
url(r'school/(?P<pk>\d+)/$', views.SchoolDetail.as_view()),


# 3.使用通用類

class SchoolView(ListCreateAPIView):
    queryset = models.School.objects.all()
    serializer_class = app01_serializers.SchoolSerializer


class SchoolDetail(RetrieveUpdateDestroyAPIView):
    queryset = models.School.objects.all()
    serializer_class = app01_serializers.SchoolSerializer

#路由
url(r'school/$', views.SchoolView.as_view()),
url(r'school/(?P<pk>\d+)/$', views.SchoolDetail.as_view()),

#4.使用視圖集

class SchoolView(ModelViewSet):
    queryset = models.School.objects.all()
    serializer_class = app01_serializers.SchoolSerializer

#路由:
url(r'school/$', views.SchoolView.as_view({
    "get": "list",
    "post": "create",
})),
url(r'school/(?P<pk>\d+)/$', views.SchoolView.as_view({
    'get': 'retrieve',
    'put': 'update',
    'patch': 'partial_update',
    'delete': 'destroy'
})),

#高級路由

from rest_framework.routers import DefaultRouter

router = DefaultRouter()
router.register(r'school', views.SchoolView)
urlpatterns += router.urls
視圖和路由

2、ModelViewSet

from django.urls import path, include
from .views import BookView, BookEditView, BookModelViewSet
urlpatterns = [
    # path('list', BookView.as_view()),
    # path('retrieve/<int:id>', BookEditView.as_view()),
    path('list', BookModelViewSet.as_view({"get": "list", "post": "create"})),
    path('retrieve/<int:pk>', BookModelViewSet.as_view({"get": "retrieve", "put": "update", "delete": "destroy"})),
]
app01中urls.py
from .models import Book
from .serializers import BookSerializer

from rest_framework.viewsets import ModelViewSet
class BookModelViewSet(ModelViewSet):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

#如今的視圖就只要寫兩行就能夠了
#注意:用框架封裝的視圖~url上的那個關鍵字參數要用pk系統默認的
##path('retrieve/<int:pk>', BookModelViewSet.as_view({"get": "retrieve", "put": "update", "delete": "destroy"}))  用pk

# from rest_framework import views
# from rest_framework import generics
# from rest_framework import mixins
# from rest_framework import viewsets
app01中views.py
from .models import Book

from rest_framework.views import APIView
from rest_framework.response import Response
from .serializers import BookSerializer

class GenericAPIView(APIView):
    query_set = None
    serializer_class = None

    def get_queryset(self):
        return self.query_set

    def get_serializer(self, *args, **kwargs):
        return self.serializer_class(*args, **kwargs)


class ListModelMixin(object):
    def list(self, request):
        queryset = self.get_queryset()
        ret = self.get_serializer(queryset, many=True)
        return Response(ret.data)


class CreateModelMixin(object):
    def create(self, request):
        serializer = self.get_serializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        else:
            return Response(serializer.errors)


class RetrieveModelMixin(object):
    def retrieve(self, request, id):
        book_obj = self.get_queryset().filter(id=id).first()
        ret = self.get_serializer(book_obj)
        return Response(ret.data)


class UpdateModelMixin(object):
    def update(self, request, id):
        book_obj = self.get_queryset().filter(id=id).first()
        serializer = self.get_serializer(book_obj, data=request.data, partial=True)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        else:
            return Response(serializer.errors)


class DestroyModelMixin(object):
    def destroy(self, request, id):
        book_obj = self.get_queryset().filter(id=id).first()
        book_obj.delete()
        return Response("")


class ListCreateAPIView(GenericAPIView, ListModelMixin, CreateModelMixin):
    pass

class RetrieveUpdateDestroyAPIView(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
    pass

# class BookView(GenericAPIView, ListModelMixin, CreateModelMixin):
class BookView(ListCreateAPIView):
    query_set = Book.objects.all()
    serializer_class = BookSerializer

    def get(self, request):
        # book_obj = Book.objects.first()
        # ret = BookSerializer(book_obj)
        # book_list = Book.objects.all()
        # book_list = self.get_queryset()
        # ret = self.get_serializer(book_list, many=True)
        # return Response(ret.data)
        return self.list(request)

    def post(self, request):
        # print(request.data)
        # serializer = BookSerializer(data=request.data)
        # if serializer.is_valid():
        #     serializer.save()
        #     return Response(serializer.data)
        # else:
        #     return Response(serializer.errors)
        return self.create(request)


# class BookEditView(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
class BookEditView(RetrieveUpdateDestroyAPIView):
    query_set = Book.objects.all()
    serializer_class = BookSerializer

    def get(self, request, id):
        # book_obj = Book.objects.filter(id=id).first()
        # ret = BookSerializer(book_obj)
        # return Response(ret.data)
        return self.retrieve(request, id)

    def put(self, request, id):
        # book_obj = Book.objects.filter(id=id).first()
        # serializer = BookSerializer(book_obj, data=request.data, partial=True)
        # if serializer.is_valid():
        #     serializer.save()
        #     return Response(serializer.data)
        # else:
        #     return Response(serializer.errors)
        return self.update(request, id)

    def delete(self, request, id):
        # book_obj = Book.objects.filter(id=id).first()
        # book_obj.delete()
        # return Response("")
        return self.destroy(request, id)



# class ViewSetMixin(object):
#     def as_view(self):
#         """
#         按照咱們參數指定的去匹配
#         get-->list
#         :return:
#         """


from rest_framework.viewsets import ViewSetMixin    #必須繼承ViewSetMixin,路由的as_view方法才能夠傳參


class ModelViewSet(ViewSetMixin, GenericAPIView, ListModelMixin, CreateModelMixin, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
    pass


#上面封裝的全部框架都幫咱們封裝好了
#from rest_framework.viewsets import ModelViewSet
#注意:用框架封裝的視圖url上的那個關鍵字參數要用pk系統默認的

class BookModelViewSet(ModelViewSet):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
本身封裝ModelViewSet

DRF的路由

from django.urls import path, include
from .views import BookView, BookEditView, BookModelViewSet
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r"book", BookModelViewSet)
urlpatterns = [
    # path('list', BookView.as_view()),
    # path('retrieve/<int:id>', BookEditView.as_view()),
    #path('list', BookModelViewSet.as_view({"get": "list", "post": "create"})),
    #path('retrieve/<int:pk>', BookModelViewSet.as_view({"get": "retrieve", "put": "update", "delete": "destroy"})),
]
urlpatterns += router.urls

#經過框架能夠把路由視圖都變的很是簡單
#可是須要自定製的時候仍是須要用APIView寫,當不須要那麼多路由的時候,也不要用這種路由註冊.
app01中urls.py

DRF的版本

隨着項目的更新,版本就愈來愈多,不可能新的版本出了,之前舊的版本就不進行維護了,就須要對版本進行控制了github

1、源碼查找

  1. APIView返回View中的view函數,而後調用dispatch方法,APIView的dispatch方法
  2. 執行self.initial方法以前是各類賦值,包括request的從新封裝賦值
     def dispatch(self, request, *args, **kwargs):
            """
            `.dispatch()` is pretty much the same as Django's regular dispatch,
            but with extra hooks for startup, finalize, and exception handling.
            """
            self.args = args
            self.kwargs = kwargs
            request = self.initialize_request(request, *args, **kwargs)
            self.request = request
            self.headers = self.default_response_headers  # deprecate?
    
            try:
                self.initial(request, *args, **kwargs)   #####
                ...
    def initial(self, request, *args, **kwargs):
                """
                Runs anything that needs to occur prior to calling the method handler.
                """
                self.format_kwarg = self.get_format_suffix(**kwargs)
    
                # Perform content negotiation and store the accepted info on the request
                neg = self.perform_content_negotiation(request)
                request.accepted_renderer, request.accepted_media_type = neg
    
                # Determine the API version, if versioning is in use.
                # 版本控制
                # self.determine_version 這個方法是找咱們本身定義的版本控制類,沒有的話返回(None,None)
                version, scheme = self.determine_version(request, *args, **kwargs)
                request.version, request.versioning_scheme = version, scheme
                ###version版本信息賦值給了 request.version  版本控制方案賦值給了 request.versioning_scheme
                ###其實這個版本控制方案~就是咱們配置的版本控制的類,也就是說,APIView經過這個方法初始化本身提供的組件
                ###接下來看看框架提供了哪些版本的控制方法在rest_framework.versioning裏,from rest_framework import versioning
    
                # Ensure that the incoming request is permitted
                # 認證 權限 頻率組件
                self.perform_authentication(request)
                self.check_permissions(request)
                self.check_throttles(request)
    
    
    ...
    
        def determine_version(self, request, *args, **kwargs):
            """
            If versioning is being used, then determine any API version for the
            incoming request. Returns a two-tuple of (version, versioning_scheme)
            """
            if self.versioning_class is None:
                return (None, None)
            #scheme是咱們配置的版本控制類的實例化對象
            scheme = self.versioning_class()
            #返回值scheme.determine_version  MyVersion中必須定義determine_version這個方法,從上面能夠看出此方法返回版本號version
            return (scheme.determine_version(request, *args, **kwargs), scheme)
    源碼查找

2、使用方法1(URL上攜帶版本信息的配置)

第1步:settings.py

REST_FRAMEWORK = {
    # 默認使用的版本控制類
    'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning',
    # 容許的版本
    'ALLOWED_VERSIONS': ['v1', 'v2'],
    # 版本使用的參數名稱
    'VERSION_PARAM': 'version',
    # 默認使用的版本
    'DEFAULT_VERSION': 'v1',
}
settings.py

第2步:app01中urls.py

urlpatterns = [
    url(r"^versions", MyView.as_view()),
    url(r"^(?P[v1|v2]+)/test01", TestView.as_view()),
]
app01中urls.py

第3步:測試視圖app01.views.py

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("根本就匹配不到這個路由")
app01.views.py

3、使用方法2(URL過濾條件配置版本信息)

第1步:settings.py

REST_FRAMEWORK = {
    "DEFAULT_VERSIONING_CLASS": "utils.version.MyVersion",
    # "DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.QueryParameterVersioning",
    "DEFAULT_VERSION": "v1",
    "ALLOWED_VERSIONS": "v1, v2",
    "VERSION_PARAM": "ver"
}
settings.py

第2步:app01中urls.py

from django.urls import path, include
from .views import DemoView

urlpatterns = [
    path(r"", DemoView.as_view()),
]
app01中urls.py

第3步:utils中version.py

from rest_framework import versioning
class MyVersion(object):
    def determine_version(self, request, *args, **kwargs):
        # 返回值 給了request.version
        # 返回版本號
        # 版本號攜帶在過濾條件 xxxx?version=v1中,版本號在那就去那取值
        version = request.query_params.get("version", "v1")
        return version
utils中version.py

第4步:測試視圖app01.views.py

from rest_framework.views import APIView
from rest_framework.response import Response

class DemoView(APIView):
    def get(self, request):
        print(request.version)
        print(request.versioning_scheme)
        # 獲得版本號  根據版本號的不一樣返回不一樣的信息
        if request.version == "v1":
            return Response("v1版本的數據")
        elif request.version == "v2":
            return Response("v2版本的數據")
        return Response("不存在的版本")
app01.views.py

DRF的認證

每次給服務器發請求,因爲Http的無狀態,致使每次都是新的請求,
服務端須要對每次來的請求進行認證,看用戶是否登陸,以及登陸用戶是誰,
服務器對每一個請求進行認證的時候,不可能在每一個視圖函數中都寫認證,
必定是把認證邏輯抽離出來,之前咱們可能會加裝飾器或者中間件。 數據庫

1、源碼查找

 def dispatch(self, request, *args, **kwargs):
        """
        `.dispatch()` is pretty much the same as Django's regular dispatch,
        but with extra hooks for startup, finalize, and exception handling.
        """
        self.args = args
        self.kwargs = kwargs
        request = self.initialize_request(request, *args, **kwargs)
        self.request = request
        self.headers = self.default_response_headers  # deprecate?

        try:
            self.initial(request, *args, **kwargs)

            # Get the appropriate handler method
            if request.method.lower() in self.http_method_names:
                handler = getattr(self, request.method.lower(),
                                  self.http_method_not_allowed)
            else:
                handler = self.http_method_not_allowed

            response = handler(request, *args, **kwargs)

        except Exception as exc:
            response = self.handle_exception(exc)

        self.response = self.finalize_response(request, response, *args, **kwargs)
        return self.response

...
    def initialize_request(self, request, *args, **kwargs):
        """
        Returns the initial request object.
        """
        parser_context = self.get_parser_context(request)

        return Request(
            request,
            parsers=self.get_parsers(),
            authenticators=self.get_authenticators(),
            negotiator=self.get_content_negotiator(),
            parser_context=parser_context
        )


    def initial(self, request, *args, **kwargs):
        """
        Runs anything that needs to occur prior to calling the method handler.
        """
        self.format_kwarg = self.get_format_suffix(**kwargs)

        # Perform content negotiation and store the accepted info on the request
        neg = self.perform_content_negotiation(request)
        request.accepted_renderer, request.accepted_media_type = neg

        # Determine the API version, if versioning is in use.
        # 版本控制
        # self.determine_version 這個方法是找咱們本身定義的版本控制類,沒有的話返回(None,None)
        version, scheme = self.determine_version(request, *args, **kwargs)
        request.version, request.versioning_scheme = version, scheme

        # Ensure that the incoming request is permitted
        # 認證 權限 頻率組件
        self.perform_authentication(request)
        self.check_permissions(request)
        self.check_throttles(request)

...
    def perform_authentication(self, request):
        """
        Perform authentication on the incoming request.

        Note that if you override this and simply 'pass', then authentication
        will instead be performed lazily, the first time either
        `request.user` or `request.auth` is accessed.
        """
        request.user
...
#去類Request中找user        

    @property
    def user(self):
        """
        Returns the user associated with the current request, as authenticated
        by the authentication classes provided to the request.
        """
        if not hasattr(self, '_user'):
            with wrap_attributeerrors():
                #__enter__
                self._authenticate()
                #__exit__
        return self._user
...

    def _authenticate(self):
        """
        Attempt to authenticate the request using each authentication instance
        in turn.
        """
        #這裏的authentications是最開始實例化Request類的時候傳過來的
        #是調用get_authenticators這個方法,
        # 這個方法的返回值是 return [auth() for auth in self,authentication_classes]
        #authentication_classes若是咱們配置了就用咱們配置的,不然就從默認配置文件中讀取配置類
        #返回的auth()是認證類實例化後的
        for authenticator in self.authenticators:   #查看authenticators
            try:
                #也就是說這裏的authenticator是認證類實例化後的
                #authenticate方法是咱們必須去實現的方法
                #authenticate的參數self,咱們是經過新的request.user進來的,因此這個self就是新的request
                user_auth_tuple = authenticator.authenticate(self)
            except exceptions.APIException:
                self._not_authenticated()
                raise

            if user_auth_tuple is not None:
                self._authenticator = authenticator
                #request.user
                #request.auth
                self.user, self.auth = user_auth_tuple
                return

        self._not_authenticated()

...
class Request(object):

    def __init__(self, request, parsers=None, authenticators=None,
                 negotiator=None, parser_context=None):
        assert isinstance(request, HttpRequest), (
            'The `request` argument must be an instance of '
            '`django.http.HttpRequest`, not `{}.{}`.'
            .format(request.__class__.__module__, request.__class__.__name__)
        )

        self._request = request
        self.parsers = parsers or ()
        self.authenticators = authenticators or ()
        ## authenticators看傳參了麼
...

    def initialize_request(self, request, *args, **kwargs):
        """
        Returns the initial request object.
        """
        parser_context = self.get_parser_context(request)

        return Request(
            request,
            parsers=self.get_parsers(),
            authenticators=self.get_authenticators(),  #傳參了
            negotiator=self.get_content_negotiator(),
            parser_context=parser_context
        )

...

    def get_authenticators(self):
        """
        Instantiates and returns the list of authenticators that this view can use.
        """
        #self.authentication_classes去配置文件拿全部的認證類
        return [auth() for auth in self.authentication_classes]
源碼查找
  • APIView的dispatch方法裏給request從新賦值了
  • APIView的dispatch方法裏給執行了initial方法,初始化了版本認證,權限,頻率組件,initial方法的參數request是從新賦值後的
  • 權限組件返回的是request.user,initial的request是從新賦值以後的,因此這裏的request是從新賦值以後的,也就是Request類實例對象, 那這個user必定是一個靜態方法.

2、使用方法

一、app01中models.py

# 先在model中註冊模型類
# 而且進行數據遷移

from django.db import models

class User(models.Model):

    username = models.CharField(max_length=32)
    pwd = models.CharField(max_length=16)
    token = models.UUIDField()
app01中models.py

二、app01中urls.py

from django.urls import path
from .views import DemoView, LoginView, TestView


urlpatterns = [
    path(r"login", LoginView.as_view()),
    path(r"test", TestView.as_view()),
]
app01中urls.py

三、app01中views.py

import uuid
from .models import User
from utils.auth import MyAuth

from rest_framework.views import APIView
from rest_framework.response import Response

class LoginView(APIView):

    def post(self, request):
        username = request.data.get("username")
        pwd = request.data.get("pwd")
        # 登陸成功 生成token 會把token給你返回
        token = uuid.uuid4()
        User.objects.create(username=username, pwd=pwd, token=token)
        return Response("建立用戶成功")

#局部視圖認證
class TestView(APIView):
    authentication_classes = [MyAuth,]

    def get(self, request):
        print(request.user)
        print(request.auth)
        user_id = request.user.id
        return Response("認證測試")
app01中views.py

四、utils中auth.py 寫一個認證的類

from rest_framework.exceptions import AuthenticationFailed
from app01.models import User
from rest_framework.authentication import BaseAuthentication


class MyAuth(BaseAuthentication):

    def authenticate(self, request):
        # 作認證 看他是否登陸
        # 拿到token,此處是從url過濾條件裏拿到token
        # 去數據庫看token是否合法
        # 合法的token可以獲取用戶信息
        token = request.query_params.get("token", "")
        if not token:
            raise AuthenticationFailed("沒有攜帶token")
        user_obj = User.objects.filter(token=token).first()
        if not user_obj:
            raise AuthenticationFailed("token不合法")
        # return (None, None)  
        return (user_obj, token) #第1個返回值是request.user 第2個返回值是request.auth
utils中auth.py

五、全局配置認證

REST_FRAMEWORK = {
    # "DEFAULT_VERSIONING_CLASS": "utils.version.MyVersion",
    "DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.QueryParameterVersioning",
    "DEFAULT_VERSION": "v1",
    "ALLOWED_VERSIONS": "v1, v2",
    "VERSION_PARAM": "ver",
    # "DEFAULT_AUTHENTICATION_CLASSES": ["utils.auth.MyAuth", ]  #全局配置
}
settings.py

DRF的權限

對某件事情決策的範圍和程度叫作權限django

1、源碼查找

    def check_permissions(self, request):
        """
        Check if the request should be permitted.
        Raises an appropriate exception if the request is not permitted.
        """
        for permission in self.get_permissions():
            #permission咱們寫的權限類的實例對象 MyPermission()
            if not permission.has_permission(request, self):
                #permission_denied是拋出異常的
                #也就是說咱們的權限類中必須有has_permission這個方法,不然就拋出異常
                self.permission_denied(
                     #message 定義異常信息
                    request, message=getattr(permission, 'message', None)
                )
源碼查找
  • 權限類必定要有has_permission方法,不然就會拋出異常,這也是框架提供的鉤子
  • rest_framework.permissions這個文件中存放了框架提供的全部權限的方法
  • BasePermission 這個是寫權限類繼承的一個基礎權限類
  • Python代碼是一行一行執行的,那麼執行initial方法初始化這些組件的時候 也是有順序的,版本在前面而後是認證,而後是權限最後是頻率
  • 版本,認證,權限,頻率這幾個組件的源碼是一個流程

2、使用方法

一、app01中models.py

# 先在model中註冊模型類
# 而且進行數據遷移

from django.db import models

class User(models.Model):

    username = models.CharField(max_length=32)
    pwd = models.CharField(max_length=16)
    token = models.UUIDField()
    type = models.IntegerField(choices=((1, "vip"), (2, "vvip"), (3, "普通")), default=3)
app01中models.py

二、app01中urls.py

from django.urls import path
from .views import DemoView, LoginView, TestView


urlpatterns = [
    path(r"login", LoginView.as_view()),
    path(r"test", TestView.as_view()),
]
app01中urls.py

三、app01中views.py

import uuid
from .models import User
from utils.auth import MyAuth
from utils.permission import MyPermission
# Create your views here.

from rest_framework.views import APIView
from rest_framework.response import Response


class DemoView(APIView):
    def get(self, request):
        return Response("認證demo~")


class LoginView(APIView):

    def post(self, request):
        username = request.data.get("username")
        pwd = request.data.get("pwd")
        # 登陸成功 生成token 會把token給你返回
        token = uuid.uuid4()
        User.objects.create(username=username, pwd=pwd, token=token)
        return Response("建立用戶成功")


class TestView(APIView):
    authentication_classes = [MyAuth,]
    permission_classes = [MyPermission, ]  #局部配置權限

    def get(self, request):
        print(request.user)
        print(request.auth)
        user_id = request.user.id
        return Response("認證測試")
app01中views.py

四、utils中permission.py 寫一個權限類

from rest_framework.permissions import BasePermission

class MyPermission(BasePermission):
    message = "您沒有權限"

    def has_permission(self, request, view):
        # 判斷用戶是否有權限
        user_obj = request.user
        if user_obj.type == 3:
            return False
        else:
            return True
utils中permission.py

五、全局配置權限

REST_FRAMEWORK = {
    # "DEFAULT_VERSIONING_CLASS": "utils.version.MyVersion",
    "DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.QueryParameterVersioning",
    "DEFAULT_VERSION": "v1",
    "ALLOWED_VERSIONS": "v1, v2",
    "VERSION_PARAM": "ver",
    # "DEFAULT_AUTHENTICATION_CLASSES": ["utils.auth.MyAuth", ]  #全局配置
}
REST_FRAMEWORK = {
    # "DEFAULT_VERSIONING_CLASS": "utils.version.MyVersion",
    # 默認使用的版本控制類
    'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning',
    # 容許的版本
    "ALLOWED_VERSIONS": "v1, v2",
    # 版本使用的參數名稱
     "VERSION_PARAM": "ver",
    # 默認使用的版本
    'DEFAULT_VERSION': 'v1',
    # 配置全局認證
    # "DEFAULT_AUTHENTICATION_CLASSES": ["utils.auth.MyAuth", ]  #全局配置
    # 配置全局權限
    "DEFAULT_PERMISSION_CLASSES": ["utils.permission.MyPermission"]
}
settings.py

DRF的頻率

開放平臺的API接口調用須要限制其頻率,以節約服務器資源和避免惡意的頻繁調用。json

1、源碼查找

    def check_throttles(self, request):
        """
        Check if request should be throttled.
        Raises an appropriate exception if the request is throttled.
        """
        #throttle 配置每一個頻率控制類的實例化對象   allow_request方法和wait方法
        for throttle in self.get_throttles():
            if not throttle.allow_request(request, self):
                self.throttled(request, throttle.wait())

...
    def get_throttles(self):
        """
        Instantiates and returns the list of throttles that this view uses.
        """
        return [throttle() for throttle in self.throttle_classes]        
源碼查找

2、頻率組件原理

DRF中的頻率控制基本原理是基於訪問次數和時間的,固然也能夠經過本身定義的方法來實現。

當請求進來,走到頻率組件的時候,DRF內部會有一個字典來記錄訪問者的IP,

以這個訪問者的IP爲key,value爲一個列表,存放訪問者每次訪問的時間,

{  IP1: [第三次訪問時間,第二次訪問時間,第一次訪問時間],}

把每次訪問最新時間放入列表的最前面,記錄這樣一個數據結構後,經過什麼方式限流呢~~

若是咱們設置的是10秒內只能訪問5次,

  -- 1,判斷訪問者的IP是否在這個請求IP的字典裏

  -- 2,保證這個列表裏都是最近10秒內的訪問的時間

      判斷當前請求時間和列表裏最先的(也就是最後的)請求時間的查

      若是差大於10秒,說明請求以及不是最近10秒內的,刪除掉,

      繼續判斷倒數第二個,直到差值小於10秒

  -- 3,判斷列表的長度(即訪問次數),是否大於咱們設置的5次,

      若是大於就限流,不然放行,並把時間放入列表的最前面。

3、使用方法

一、app01中views.py

import uuid
from .models import User
from utils.auth import MyAuth
from utils.permission import MyPermission
from utils.throttle import MyThrottle
# Create your views here.

from rest_framework.views import APIView
from rest_framework.response import Response

class LoginView(APIView):

    def post(self, request):
        username = request.data.get("username")
        pwd = request.data.get("pwd")
        # 登陸成功 生成token 會把token給你返回
        token = uuid.uuid4()
        User.objects.create(username=username, pwd=pwd, token=token)
        return Response("建立用戶成功")


class TestView(APIView):
    authentication_classes = [MyAuth,]
    permission_classes = [MyPermission, ]
    throttle_classes = [MyThrottle, ]

    def get(self, request):
        print(request.user)
        print(request.auth)
        user_id = request.user.id
        return Response("認證測試")
app01中views.py

二、utils中throttle.py 寫一個頻率類

from rest_framework.throttling import BaseThrottle, SimpleRateThrottle
import time

VISIT_RECORD = {}

#自定義的頻率限制類

# class MyThrottle(BaseThrottle):
#
#     def __init__(self):
#         self.history = None
#
#     def allow_request(self, request, view):
#         # 實現限流的邏輯
#         # 以IP限流
#         # 訪問列表 {IP: [time1, time2, time3]}
#         # 1, 獲取請求的IP地址
#         ip = request.META.get("REMOTE_ADDR")
#         # 2,判斷IP地址是否在訪問列表
#         now = time.time()
#         if ip not in VISIT_RECORD:
#             # --1, 不在 須要給訪問列表添加key,value
#             VISIT_RECORD[ip] = [now,]
#             return True
#             # --2 在 須要把這個IP的訪問記錄 把當前時間加入到列表
#         history = VISIT_RECORD[ip]
#         history.insert(0, now)
#         # 3, 確保列表裏最新訪問時間以及最老的訪問時間差 是1分鐘
#         while history and history[0] - history[-1] > 60:
#             history.pop()
#         self.history = history
#         # 4,獲得列表長度,判斷是不是容許的次數
#         if len(history) > 3:
#             return False
#         else:
#             return True
#
#     def wait(self):
#         # 返回須要再等多久才能訪問
#         time = 60 - (self.history[0] - self.history[-1])
#         return time

#使用自帶的頻率限制類
class MyThrottle(SimpleRateThrottle):
    scope = "WD"

    def get_cache_key(self, request, view):
        # 若是以IP地址作限流返回IP地址
        key = self.get_ident(request)
        return key
utils中throttle.py

三、全局配置頻率

REST_FRAMEWORK = {
    # "DEFAULT_VERSIONING_CLASS": "utils.version.MyVersion",
    "DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.QueryParameterVersioning",
    "DEFAULT_VERSION": "v1",
    "ALLOWED_VERSIONS": "v1, v2",
    "VERSION_PARAM": "ver",
    # "DEFAULT_AUTHENTICATION_CLASSES": ["utils.auth.MyAuth", ]  #全局配置
}
REST_FRAMEWORK = {
    # "DEFAULT_VERSIONING_CLASS": "utils.version.MyVersion",
    # 默認使用的版本控制類
    'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning',
    # 容許的版本
    "ALLOWED_VERSIONS": "v1, v2",
    # 版本使用的參數名稱
     "VERSION_PARAM": "ver",
    # 默認使用的版本
    'DEFAULT_VERSION': 'v1',
    # 配置全局認證
    # "DEFAULT_AUTHENTICATION_CLASSES": ["utils.auth.MyAuth", ]  #全局配置
    # 配置全局權限
    "DEFAULT_PERMISSION_CLASSES": ["utils.permission.MyPermission"],
    # 配置自定義頻率限制
    "DEFAULT_THROTTLE_CLASSES": ["Throttle.throttle.MyThrottle"],
    # 配置頻率限制
    "DEFAULT_THROTTLE_RATES": {
        "WD": "3/m"   #速率配置每分鐘不能超過3次訪問,WD是scope定義的值,
    }
}
settings.py

DRF的分頁組件

  • DRF提供的三種分頁: from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination, CursorPagination
  • 全局配置: REST_FRAMEWORK = { 'PAGE_SIZE': 2 }
  • 第1種 PageNumberPagination 看第n頁,每頁顯示n條數據 http://127.0.0.1:8000/book?page=2&size=1
  • 第2種 LimitOffsetPagination 在第n個位置 向後查看n條數據 http://127.0.0.1:8000/book?offset=2&limit=1
  • 第3種 CursorPagination 加密遊標的分頁 把上一頁和下一頁的id記住 http://127.0.0.1:8000/book?page=2&size=1

1、utils中pagination.py(自定義分頁類)

from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination, CursorPagination

# class MyPagination(PageNumberPagination):
#     # xxxx?page=1&size=2
#     page_size = 1  # 每頁顯示多少條
#     page_query_param = "page"   # URL中頁碼的參數
#     page_size_query_param = "size"  # URL中每頁顯示條數的參數
#     max_page_size = 3    # 最大頁碼數限制

# class MyPagination(LimitOffsetPagination):
#
#     default_limit = 1
#     limit_query_param = "limit"
#     offset_query_param = "offset"
#     max_limit = 3

class MyPagination(CursorPagination):

    cursor_query_param = "cursor"
    page_size = 2
    ordering = "-id"
utils中pagination.py

2、app01中views.py

from django.shortcuts import render
from rest_framework.views import APIView
from rest_framework.response import Response
from SerDemo.models import Book
from SerDemo.serializers import BookSerializer

# Create your views here.
from rest_framework import pagination
from utils.pagination import MyPagination
from rest_framework.generics import GenericAPIView
from rest_framework.mixins import ListModelMixin


# class BookView(APIView):
#
#     def get(self, request):
#         queryset = Book.objects.all()
#         # 1,實例化分頁器對象
#         page_obj = MyPagination()
#         # 2,調用分頁方法去分頁queryset
#         page_queryset = page_obj.paginate_queryset(queryset, request, view=self)
#         # 3,把分頁好的數據序列化返回
#         # 4, 帶着上一頁下一頁鏈接的響應
#         ser_obj = BookSerializer(page_queryset, many=True)
#         # 返回帶超連接 需返回的時候用內置的響應方法
#         return page_obj.get_paginated_response(ser_obj.data)


class BookView(GenericAPIView, ListModelMixin):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    pagination_class = MyPagination
    # self.paginate_queryset(queryset)

    def get(self, request):
        return self.list(request)
app01中views.py

DRF的解析器

  • 解析器的做用就是服務端接收客戶端傳過來的數據,把數據解析成本身想要的數據類型的過程。本質就是對請求體中的數據進行解析。
  • 在瞭解解析器以前要先知道Accept以及ContentType請求頭。
  • Accept是告訴對方能解析什麼樣的數據,一般也能夠表示想要什麼樣的數據。
  • ContentType是告訴對方我給你的是什麼樣的數據類型。
  • 解析器工做原理的本質 就是拿到請求的ContentType來判斷前端給後端數據類型是什麼,而後後端去拿相應的解析器去解析數據。

1、Django的解析器

  • 請求進來請求體中的數據在request.body中,那也就證實,解析器會把解析好的數據放入request.body
  • 在視圖中能夠打印request的類型,可以知道request是WSGIRequest這個類。
  • application/x-www-form-urlencoded不是不能上傳文件,是隻能上傳文本格式的文件
  • multipart/form-data是將文件以二進制的形式上傳,這樣能夠實現多種類型的文件上傳 一個解析到request.POST, request.FILES中。
  • 也就是說以前能在request中能到的各類數據是由於用了不一樣格式的數據解析器
  • Django只能解析cont_type=multipart/form-data 和cont_type=application/x-www-form-urlencoded的數據,不能解析json

2、DRF的解析器

  • 在request.data拿數據的時候解析器會被調用

4、DRF的解析器使用方法

一、app01中views.py

from django.shortcuts import render
from django.views import View
from django.http import HttpResponse
from django.core.handlers.wsgi import WSGIRequest
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.negotiation import DefaultContentNegotiation
from rest_framework import parsers

# Create your views here.


class DjangoView(View):
    def get(self, request):
        print(type(request))
        # Request
        # request.GET
        # request.POST
        # json request.body
        return HttpResponse("django解析器測試~~")


class DRFView(APIView):
    #parser_classes = [parsers.JSONParser, ]  #通常不配置

    def get(self, request):
        # request 從新封裝的request  Request
        # request.data
        #
        return Response("DRF解析器的測試~~")
app01中views.py

DRF的渲染器

渲染器就是友好的展現數據,咱們在瀏覽器中展現的DRF測試的那個頁面就是經過瀏覽器的渲染器來作到的,固然咱們能夠展現Json數據類型後端

DEFAULTS = {
    # Base API policies
    'DEFAULT_RENDERER_CLASSES': (
        'rest_framework.renderers.JSONRenderer',
        'rest_framework.renderers.BrowsableAPIRenderer',
    ),
DRF的渲染器
相關文章
相關標籤/搜索