日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Django REST framework 1

發布時間:2025/7/14 编程问答 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Django REST framework 1 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Django REST framework

Django REST framework官方文檔:點擊??中文文檔:點擊

  • 安裝djangorestframework:pip3 install djangorestframework (pip3 list 查看詳情和版本信息)
  • 注冊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格式,那就需要對從數據庫拿到的數據進行序列化。

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

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

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

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

    from rest_framework import serializers from .models import Bookclass 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 valueclass 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) #choicesw_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) #ManyToManyFieldauthor_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#驗證#1、單個字段的驗證def validate_title(self, value):if "python" not in value.lower():raise serializers.ValidationError("標題必須含有python")return value#2、多個字段的驗證def validate(self, attrs):if attrs["w_category"] == 1 and attrs["publisher_id"] == 1:return attrselse:raise serializers.ValidationError("分類以及標題不符合要求")#3、驗證器 validators app01中serializers.py

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

    from .models import Bookfrom rest_framework.views import APIView from rest_framework.viewsets import GenericViewSet from rest_framework.response import Response from .serializers import BookSerializerclass 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) #多個ManyToManyFieldreturn 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

    二、ModelSerializer

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

    1、定義一個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 valueclass BookSerializer(serializers.ModelSerializer):#category = serializers.CharField(source="get_category_display",read_only=True) #自定制category_display = serializers.SerializerMethodField(read_only=True) # SerializerMethodFieldpublisher_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.publisherreturn {"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 modelsclass 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'] = Truedata['price'] = price_obj.priceelse:# 沒有永久有效的價格策略data['has_price'] = Falsereturn dataclass Meta:model = models.Coursefields = '__all__'class CourseCategoryModelSerializer(serializers.ModelSerializer):class Meta:model = models.CourseCategoryfields = '__all__' to_representation

    三、Serializer和ModelSerializer的區別

    四、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.titleclass Meta:verbose_name_plural = "01-圖書表"db_table = verbose_name_pluralclass Publisher(models.Model):title = models.CharField(max_length=32, verbose_name="出版社的名稱")def __str__(self):return self.titleclass Meta:verbose_name_plural = "02-出版社表"db_table = verbose_name_pluralclass Author(models.Model):name = models.CharField(max_length=32, verbose_name="作者的姓名")def __str__(self):return self.nameclass 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, Publisherclass 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來獲取。

    一、源碼查找

  • django中寫CBV的時候繼承的是View,rest_framework繼承的是APIView
  • 不管是View還是APIView最開始調用的都是as_view()方法。
  • APIView繼承了View, 并且執行了View中的as_view()方法,最后用csrf_exempt()方法包裹view把view返回了,用csrf_exempt()方法包裹后去掉了csrf的認證。
  • 在View中的as_view方法返回了view函數,而view函數執行了self.dispatch()方法,但是這里是APIView調用的,所以先從APIView找dispatch()方法。
  • APIView的dispatch()方法中給request重新賦值了
  • 去initialize_request中看下把什么賦值給了request,并且賦值給了self.request, 也就是視圖中用的request.xxx到底是什么
  • 可以看到,這個方法返回的是Request這個類的實例對象,注意看下這個Request類中的第一個參數request,是走django的時候的原來的request
  • 去Request這個類里看 這個Request類把原來的request賦值給了self._request, 也就是說以后_request是老的request,新的request是這個Request類
  • request.query_params 存放的是get請求的參數 request.data 存放的是所有的數據,包括post請求的以及put,patch請求
  • 相比原來的django的request,現在的request更加精簡,清晰了
  • 框架提供了一個路由傳參的方法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.SchoolSerializerdef 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.SchoolSerializerdef 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.SchoolSerializerclass 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 DefaultRouterrouter = DefaultRouter() router.register(r'school', views.SchoolView) urlpatterns += router.urls 視圖和路由

    二、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 BookSerializerfrom 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 Bookfrom rest_framework.views import APIView from rest_framework.response import Response from .serializers import BookSerializerclass GenericAPIView(APIView):query_set = Noneserializer_class = Nonedef get_queryset(self):return self.query_setdef 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):passclass RetrieveUpdateDestroyAPIView(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):pass# class BookView(GenericAPIView, ListModelMixin, CreateModelMixin): class BookView(ListCreateAPIView):query_set = Book.objects.all()serializer_class = BookSerializerdef 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 = BookSerializerdef 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的版本

    隨著項目的更新,版本就越來越多,不可能新的版本出了,以前舊的版本就不進行維護了,就需要對版本進行控制了

    一、源碼查找

  • APIView返回View中的view函數,然后調用dispatch方法,APIView的dispatch方法
  • 執行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 = argsself.kwargs = kwargsrequest = self.initialize_request(request, *args, **kwargs)self.request = requestself.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 requestneg = 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 theincoming 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這個方法,從上面可以看出此方法返回版本號versionreturn (scheme.determine_version(request, *args, **kwargs), scheme) 源碼查找
  • 二、使用方法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.versionif ret == "v1":return Response("版本v1的信息")elif ret == "v2":return Response("版本v2的信息")else:return Response("根本就匹配不到這個路由") app01.views.py

    三、使用方法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 DemoViewurlpatterns = [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 Responseclass 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的無狀態,導致每次都是新的請求,
    服務端需要對每次來的請求進行認證,看用戶是否登錄,以及登錄用戶是誰,
    服務器對每個請求進行認證的時候,不可能在每個視圖函數中都寫認證,
    一定是把認證邏輯抽離出來,以前我們可能會加裝飾器或者中間件。

    一、源碼查找

    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 = argsself.kwargs = kwargsrequest = self.initialize_request(request, *args, **kwargs)self.request = requestself.headers = self.default_response_headers # deprecate?try:self.initial(request, *args, **kwargs)# Get the appropriate handler methodif 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_allowedresponse = 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 requestneg = 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 authenticationwill instead be performed lazily, the first time either`request.user` or `request.auth` is accessed."""request.user ... #去類Request中找user @propertydef user(self):"""Returns the user associated with the current request, as authenticatedby 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 instancein turn."""#這里的authentications是最開始實例化Request類的時候傳過來的#是調用get_authenticators這個方法,# 這個方法的返回值是 return [auth() for auth in self,authentication_classes]#authentication_classes如果我們配置了就用我們配置的,否則就從默認配置文件中讀取配置類#返回的auth()是認證類實例化后的for authenticator in self.authenticators: #查看authenticatorstry:#也就是說這里的authenticator是認證類實例化后的#authenticate方法是我們必須去實現的方法#authenticate的參數self,我們是通過新的request.user進來的,所以這個self就是新的requestuser_auth_tuple = authenticator.authenticate(self)except exceptions.APIException:self._not_authenticated()raiseif user_auth_tuple is not None:self._authenticator = authenticator#request.user#request.authself.user, self.auth = user_auth_tuplereturnself._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 = requestself.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一定是一個靜態方法.

    二、使用方法

    1、app01中models.py

    # 先在model中注冊模型類 # 并且進行數據遷移from django.db import modelsclass User(models.Model):username = models.CharField(max_length=32)pwd = models.CharField(max_length=16)token = models.UUIDField() app01中models.py

    2、app01中urls.py

    from django.urls import path from .views import DemoView, LoginView, TestViewurlpatterns = [path(r"login", LoginView.as_view()),path(r"test", TestView.as_view()), ] app01中urls.py

    3、app01中views.py

    import uuid from .models import User from utils.auth import MyAuthfrom rest_framework.views import APIView from rest_framework.response import Responseclass 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.idreturn Response("認證測試") app01中views.py

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

    from rest_framework.exceptions import AuthenticationFailed from app01.models import User from rest_framework.authentication import BaseAuthenticationclass 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

    5、全局配置認證

    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的權限

    對某件事情決策的范圍和程度叫做權限

    一、源碼查找

    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方法初始化這些組件的時候 也是有順序的,版本在前面然后是認證,然后是權限最后是頻率
    • 版本,認證,權限,頻率這幾個組件的源碼是一個流程

    二、使用方法

    1、app01中models.py

    # 先在model中注冊模型類 # 并且進行數據遷移from django.db import modelsclass 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

    2、app01中urls.py

    from django.urls import path from .views import DemoView, LoginView, TestViewurlpatterns = [path(r"login", LoginView.as_view()),path(r"test", TestView.as_view()), ] app01中urls.py

    3、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 Responseclass 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.idreturn Response("認證測試") app01中views.py

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

    from rest_framework.permissions import BasePermissionclass MyPermission(BasePermission):message = "您沒有權限"def has_permission(self, request, view):# 判斷用戶是否有權限user_obj = request.userif user_obj.type == 3:return Falseelse:return True utils中permission.py

    5、全局配置權限

    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接口調用需要限制其頻率,以節約服務器資源和避免惡意的頻繁調用。

    一、源碼查找

    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] 源碼查找

    二、頻率組件原理

    DRF中的頻率控制基本原理是基于訪問次數和時間的,當然也可以通過自己定義的方法來實現。當請求進來,走到頻率組件的時候,DRF內部會有一個字典來記錄訪問者的IP,以這個訪問者的IP為key,value為一個列表,存放訪問者每次訪問的時間,{ IP1: [第三次訪問時間,第二次訪問時間,第一次訪問時間],}把每次訪問最新時間放入列表的最前面,記錄這樣一個數據結構后,通過什么方式限流呢~~如果我們設置的是10秒內只能訪問5次,-- 1,判斷訪問者的IP是否在這個請求IP的字典里-- 2,保證這個列表里都是最近10秒內的訪問的時間判斷當前請求時間和列表里最早的(也就是最后的)請求時間的查如果差大于10秒,說明請求以及不是最近10秒內的,刪除掉,繼續判斷倒數第二個,直到差值小于10秒-- 3,判斷列表的長度(即訪問次數),是否大于我們設置的5次,如果大于就限流,否則放行,并把時間放入列表的最前面。

    三、使用方法

    1、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 Responseclass 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.idreturn Response("認證測試") app01中views.py

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

    from rest_framework.throttling import BaseThrottle, SimpleRateThrottle import timeVISIT_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

    3、全局配置頻率

    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

    一、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 = 3class MyPagination(CursorPagination):cursor_query_param = "cursor"page_size = 2ordering = "-id" utils中pagination.py

    二、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 = BookSerializerpagination_class = MyPagination# self.paginate_queryset(queryset)def get(self, request):return self.list(request) app01中views.py

    DRF的解析器

    • 解析器的作用就是服務端接收客戶端傳過來的數據,把數據解析成自己想要的數據類型的過程。本質就是對請求體中的數據進行解析。
    • 在了解解析器之前要先知道Accept以及ContentType請求頭。
    • Accept是告訴對方能解析什么樣的數據,通常也可以表示想要什么樣的數據。
    • ContentType是告訴對方我給你的是什么樣的數據類型。
    • 解析器工作原理的本質 就是拿到請求的ContentType來判斷前端給后端數據類型是什么,然后后端去拿相應的解析器去解析數據。

    一、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

    二、DRF的解析器

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

    四、DRF的解析器使用方法

    1、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.bodyreturn 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的渲染器

    轉載于:https://www.cnblogs.com/bubu99/p/10487215.html

    總結

    以上是生活随笔為你收集整理的Django REST framework 1的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。