Django REST framework 1
Django REST framework
Django REST framework官方文檔:點(diǎn)擊??中文文檔:點(diǎn)擊
DRF序列化
做前后端分離的項目,前后端交互一般都選擇JSON數(shù)據(jù)格式,JSON是一個輕量級的數(shù)據(jù)交互格式。后端給前端數(shù)據(jù)的時候都要轉(zhuǎn)成json格式,那就需要對從數(shù)據(jù)庫拿到的數(shù)據(jù)進(jìn)行序列化。
要用DRF的序列化,就要遵循人家框架的一些標(biāo)準(zhǔn)
DRF反序列化
- 當(dāng)前端給后端發(fā)post的請求的時候,前端給后端傳過來的數(shù)據(jù),要進(jìn)行一些校驗然后保存到數(shù)據(jù)庫。
- 這些校驗以及保存工作,DRF的Serializer也提供了一些方法了,
- 首先要寫反序列化用的一些字段,有些字段要跟序列化區(qū)分開,
- 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 定義函數(shù)寫驗證規(guī)則 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權(quán)重高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()#外鍵關(guān)系的序列化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("標(biāo)題必須含有python")return value#2、多個字段的驗證def validate(self, attrs):if attrs["w_category"] == 1 and attrs["publisher_id"] == 1:return attrselse:raise serializers.ValidationError("分類以及標(biāo)題不符合要求")#3、驗證器 validators app01中serializers.py2、用聲明的序列化類去序列化(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允許部分進(jìn)行更新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
- 跟模型緊密相關(guān)的序列化器
- 根據(jù)模型自動生成一組字段
- 默認(rèn)實(shí)現(xiàn)了.update()以及.create()方法
1、定義一個ModelSerializer序列化器(app01中serializers.py)
from rest_framework import serializers from .models import Book# 驗證器 validators 定義函數(shù)寫驗證規(guī)則 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是當(dāng)前序列化的book對象#外鍵關(guān)聯(lián)的對象有很多字段是用不到的~都傳給前端會有數(shù)據(jù)冗余~就需要去定制序列化外鍵對象的哪些字段~~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對象# 外鍵關(guān)聯(lián)的對象有很多字段是用不到的~都傳給前端會有數(shù)據(jù)冗余~就需要去定制序列化外鍵對象的哪些字段~~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 代表找嵌套關(guān)系的第1層#注意:當(dāng)序列化類MATE中定義了depth時,這個序列化類中引用字段(外鍵)則自動變?yōu)橹蛔x#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()# 修改序列化結(jié)果的終極方法def to_representation(self, instance):# 調(diào)用父類的同名方法把序列化的結(jié)果拿到data = super().to_representation(instance)# 針對序列化的結(jié)果做一些自定制操作# 判斷當(dāng)前這個課程是否有永久有效的價格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的區(qū)別
四、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實(shí)現(xiàn)序列化def get(self, request):book_list = Book.objects.values("id", "title", "category", "pub_time", "publisher")book_list = list(book_list)# 如果需要取外鍵關(guān)聯(lián)的字段信息 需要循環(huán)獲取外鍵 再去數(shù)據(jù)庫查然后拼接成想要的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實(shí)現(xiàn)序列化# 能夠得到要的效果,但是結(jié)構(gòu)有點(diǎn)復(fù)雜,而且choices不能顯示對應(yīng)的文本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中內(nèi)置的Request類擴(kuò)展了Django中的Request類,實(shí)現(xiàn)了很多方便的功能--如請求數(shù)據(jù)解析和認(rèn)證等。
- 比如,區(qū)別于Django中的request從request.GET中獲取URL參數(shù),從request.POST中取某些情況下的POST數(shù)據(jù)。
- 在APIView中封裝的request,就實(shí)現(xiàn)了請求數(shù)據(jù)的解析:
- 對于GET請求的參數(shù)通過request.query_params來獲取。
- 對于POST請求、PUT請求的數(shù)據(jù)通過request.data來獲取。
一、源碼查找
二、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#現(xiàn)在的視圖就只要寫兩行就可以了 #注意:用框架封裝的視圖~url上的那個關(guān)鍵字參數(shù)要用pk系統(tǒng)默認(rèn)的 ##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): # """ # 按照我們參數(shù)指定的去匹配 # 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上的那個關(guān)鍵字參數(shù)要用pk系統(tǒng)默認(rèn)的class BookModelViewSet(ModelViewSet):queryset = Book.objects.all()serializer_class = BookSerializer 自己封裝ModelViewSetDRF的路由
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寫,當(dāng)不需要那么多路由的時候,也不要用這種路由注冊. app01中urls.pyDRF的版本
隨著項目的更新,版本就越來越多,不可能新的版本出了,以前舊的版本就不進(jìn)行維護(hù)了,就需要對版本進(jìn)行控制了
一、源碼查找
二、使用方法1(URL上攜帶版本信息的配置)
第1步:settings.py
REST_FRAMEWORK = {# 默認(rèn)使用的版本控制類'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning',# 允許的版本'ALLOWED_VERSIONS': ['v1', 'v2'],# 版本使用的參數(shù)名稱'VERSION_PARAM': 'version',# 默認(rèn)使用的版本'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)# 得到版本號 根據(jù)版本號的不同返回不同的信息if request.version == "v1":return Response("v1版本的數(shù)據(jù)")elif request.version == "v2":return Response("v2版本的數(shù)據(jù)")return Response("不存在的版本") app01.views.pyDRF的認(rèn)證
每次給服務(wù)器發(fā)請求,由于Http的無狀態(tài),導(dǎo)致每次都是新的請求,
服務(wù)端需要對每次來的請求進(jìn)行認(rèn)證,看用戶是否登錄,以及登錄用戶是誰,
服務(wù)器對每個請求進(jìn)行認(rèn)證的時候,不可能在每個視圖函數(shù)中都寫認(rèn)證,
一定是把認(rèn)證邏輯抽離出來,以前我們可能會加裝飾器或者中間件。
一、源碼查找
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# 認(rèn)證 權(quán)限 頻率組件 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是最開始實(shí)例化Request類的時候傳過來的#是調(diào)用get_authenticators這個方法,# 這個方法的返回值是 return [auth() for auth in self,authentication_classes]#authentication_classes如果我們配置了就用我們配置的,否則就從默認(rèn)配置文件中讀取配置類#返回的auth()是認(rèn)證類實(shí)例化后的for authenticator in self.authenticators: #查看authenticatorstry:#也就是說這里的authenticator是認(rèn)證類實(shí)例化后的#authenticate方法是我們必須去實(shí)現(xiàn)的方法#authenticate的參數(shù)self,我們是通過新的request.user進(jìn)來的,所以這個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去配置文件拿所有的認(rèn)證類return [auth() for auth in self.authentication_classes] 源碼查找- APIView的dispatch方法里給request重新賦值了
- APIView的dispatch方法里給執(zhí)行了initial方法,初始化了版本認(rèn)證,權(quán)限,頻率組件,initial方法的參數(shù)request是重新賦值后的
- 權(quán)限組件返回的是request.user,initial的request是重新賦值之后的,所以這里的request是重新賦值之后的,也就是Request類實(shí)例對象, 那這個user一定是一個靜態(tài)方法.
二、使用方法
1、app01中models.py
# 先在model中注冊模型類 # 并且進(jìn)行數(shù)據(jù)遷移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.py2、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.py3、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("創(chuàng)建用戶成功")#局部視圖認(rèn)證 class TestView(APIView):authentication_classes = [MyAuth,]def get(self, request):print(request.user)print(request.auth)user_id = request.user.idreturn Response("認(rèn)證測試") app01中views.py4、utils中auth.py 寫一個認(rèn)證的類
from rest_framework.exceptions import AuthenticationFailed from app01.models import User from rest_framework.authentication import BaseAuthenticationclass MyAuth(BaseAuthentication):def authenticate(self, request):# 做認(rèn)證 看他是否登錄# 拿到token,此處是從url過濾條件里拿到token# 去數(shù)據(jù)庫看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.py5、全局配置認(rèn)證
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.pyDRF的權(quán)限
對某件事情決策的范圍和程度叫做權(quán)限
一、源碼查找
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我們寫的權(quán)限類的實(shí)例對象 MyPermission()if not permission.has_permission(request, self):#permission_denied是拋出異常的#也就是說我們的權(quán)限類中必須有has_permission這個方法,否則就拋出異常 self.permission_denied(#message 定義異常信息request, message=getattr(permission, 'message', None)) 源碼查找- 權(quán)限類一定要有has_permission方法,否則就會拋出異常,這也是框架提供的鉤子
- rest_framework.permissions這個文件中存放了框架提供的所有權(quán)限的方法
- BasePermission 這個是寫權(quán)限類繼承的一個基礎(chǔ)權(quán)限類
- Python代碼是一行一行執(zhí)行的,那么執(zhí)行initial方法初始化這些組件的時候 也是有順序的,版本在前面然后是認(rèn)證,然后是權(quán)限最后是頻率
- 版本,認(rèn)證,權(quán)限,頻率這幾個組件的源碼是一個流程
二、使用方法
1、app01中models.py
# 先在model中注冊模型類 # 并且進(jìn)行數(shù)據(jù)遷移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.py2、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.py3、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("認(rèn)證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("創(chuàng)建用戶成功")class TestView(APIView):authentication_classes = [MyAuth,]permission_classes = [MyPermission, ] #局部配置權(quán)限def get(self, request):print(request.user)print(request.auth)user_id = request.user.idreturn Response("認(rèn)證測試") app01中views.py4、utils中permission.py 寫一個權(quán)限類
from rest_framework.permissions import BasePermissionclass MyPermission(BasePermission):message = "您沒有權(quán)限"def has_permission(self, request, view):# 判斷用戶是否有權(quán)限user_obj = request.userif user_obj.type == 3:return Falseelse:return True utils中permission.py5、全局配置權(quán)限
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",# 默認(rèn)使用的版本控制類'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning',# 允許的版本"ALLOWED_VERSIONS": "v1, v2",# 版本使用的參數(shù)名稱"VERSION_PARAM": "ver",# 默認(rèn)使用的版本'DEFAULT_VERSION': 'v1',# 配置全局認(rèn)證# "DEFAULT_AUTHENTICATION_CLASSES": ["utils.auth.MyAuth", ] #全局配置# 配置全局權(quán)限"DEFAULT_PERMISSION_CLASSES": ["utils.permission.MyPermission"] } settings.pyDRF的頻率
開放平臺的API接口調(diào)用需要限制其頻率,以節(jié)約服務(wù)器資源和避免惡意的頻繁調(diào)用。
一、源碼查找
def check_throttles(self, request):"""Check if request should be throttled.Raises an appropriate exception if the request is throttled."""#throttle 配置每個頻率控制類的實(shí)例化對象 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中的頻率控制基本原理是基于訪問次數(shù)和時間的,當(dāng)然也可以通過自己定義的方法來實(shí)現(xiàn)。當(dāng)請求進(jìn)來,走到頻率組件的時候,DRF內(nèi)部會有一個字典來記錄訪問者的IP,以這個訪問者的IP為key,value為一個列表,存放訪問者每次訪問的時間,{ IP1: [第三次訪問時間,第二次訪問時間,第一次訪問時間],}把每次訪問最新時間放入列表的最前面,記錄這樣一個數(shù)據(jù)結(jié)構(gòu)后,通過什么方式限流呢~~如果我們設(shè)置的是10秒內(nèi)只能訪問5次,-- 1,判斷訪問者的IP是否在這個請求IP的字典里-- 2,保證這個列表里都是最近10秒內(nèi)的訪問的時間判斷當(dāng)前請求時間和列表里最早的(也就是最后的)請求時間的查如果差大于10秒,說明請求以及不是最近10秒內(nèi)的,刪除掉,繼續(xù)判斷倒數(shù)第二個,直到差值小于10秒-- 3,判斷列表的長度(即訪問次數(shù)),是否大于我們設(shè)置的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("創(chuàng)建用戶成功")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("認(rèn)證測試") app01中views.py2、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): # # 實(shí)現(xiàn)限流的邏輯 # # 以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的訪問記錄 把當(dāng)前時間加入到列表 # history = VISIT_RECORD[ip] # history.insert(0, now) # # 3, 確保列表里最新訪問時間以及最老的訪問時間差 是1分鐘 # while history and history[0] - history[-1] > 60: # history.pop() # self.history = history # # 4,得到列表長度,判斷是否是允許的次數(shù) # 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.py3、全局配置頻率
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",# 默認(rèn)使用的版本控制類'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning',# 允許的版本"ALLOWED_VERSIONS": "v1, v2",# 版本使用的參數(shù)名稱"VERSION_PARAM": "ver",# 默認(rèn)使用的版本'DEFAULT_VERSION': 'v1',# 配置全局認(rèn)證# "DEFAULT_AUTHENTICATION_CLASSES": ["utils.auth.MyAuth", ] #全局配置# 配置全局權(quán)限"DEFAULT_PERMISSION_CLASSES": ["utils.permission.MyPermission"],# 配置自定義頻率限制"DEFAULT_THROTTLE_CLASSES": ["Throttle.throttle.MyThrottle"],# 配置頻率限制"DEFAULT_THROTTLE_RATES": {"WD": "3/m" #速率配置每分鐘不能超過3次訪問,WD是scope定義的值, } } settings.pyDRF的分頁組件
- DRF提供的三種分頁: from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination, CursorPagination
- 全局配置: REST_FRAMEWORK = { 'PAGE_SIZE': 2 }
- 第1種 PageNumberPagination 看第n頁,每頁顯示n條數(shù)據(jù) http://127.0.0.1:8000/book?page=2&size=1
- 第2種 LimitOffsetPagination 在第n個位置 向后查看n條數(shù)據(jù) http://127.0.0.1:8000/book?offset=2&limit=1
- 第3種 CursorPagination 加密游標(biāo)的分頁 把上一頁和下一頁的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中頁碼的參數(shù) # page_size_query_param = "size" # URL中每頁顯示條數(shù)的參數(shù) # max_page_size = 3 # 最大頁碼數(shù)限制# 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,實(shí)例化分頁器對象 # page_obj = MyPagination() # # 2,調(diào)用分頁方法去分頁queryset # page_queryset = page_obj.paginate_queryset(queryset, request, view=self) # # 3,把分頁好的數(shù)據(jù)序列化返回 # # 4, 帶著上一頁下一頁連接的響應(yīng) # ser_obj = BookSerializer(page_queryset, many=True) # # 返回帶超鏈接 需返回的時候用內(nèi)置的響應(yīng)方法 # 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.pyDRF的解析器
- 解析器的作用就是服務(wù)端接收客戶端傳過來的數(shù)據(jù),把數(shù)據(jù)解析成自己想要的數(shù)據(jù)類型的過程。本質(zhì)就是對請求體中的數(shù)據(jù)進(jìn)行解析。
- 在了解解析器之前要先知道Accept以及ContentType請求頭。
- Accept是告訴對方能解析什么樣的數(shù)據(jù),通常也可以表示想要什么樣的數(shù)據(jù)。
- ContentType是告訴對方我給你的是什么樣的數(shù)據(jù)類型。
- 解析器工作原理的本質(zhì) 就是拿到請求的ContentType來判斷前端給后端數(shù)據(jù)類型是什么,然后后端去拿相應(yīng)的解析器去解析數(shù)據(jù)。
一、Django的解析器
- 請求進(jìn)來請求體中的數(shù)據(jù)在request.body中,那也就證明,解析器會把解析好的數(shù)據(jù)放入request.body
- 在視圖中可以打印request的類型,能夠知道request是WSGIRequest這個類。
- application/x-www-form-urlencoded不是不能上傳文件,是只能上傳文本格式的文件
- multipart/form-data是將文件以二進(jìn)制的形式上傳,這樣可以實(shí)現(xiàn)多種類型的文件上傳 一個解析到request.POST, request.FILES中。
- 也就是說之前能在request中能到的各種數(shù)據(jù)是因為用了不同格式的數(shù)據(jù)解析器
- Django只能解析cont_type=multipart/form-data 和cont_type=application/x-www-form-urlencoded的數(shù)據(jù),不能解析json
二、DRF的解析器
- 在request.data拿數(shù)據(jù)的時候解析器會被調(diào)用
四、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.pyDRF的渲染器
渲染器就是友好的展示數(shù)據(jù),我們在瀏覽器中展示的DRF測試的那個頁面就是通過瀏覽器的渲染器來做到的,當(dāng)然我們可以展示Json數(shù)據(jù)類型
DEFAULTS = {# Base API policies'DEFAULT_RENDERER_CLASSES': ('rest_framework.renderers.JSONRenderer','rest_framework.renderers.BrowsableAPIRenderer',), DRF的渲染器轉(zhuǎn)載于:https://www.cnblogs.com/bubu99/p/10487215.html
總結(jié)
以上是生活随笔為你收集整理的Django REST framework 1的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C及C++中typedef的简单使用指南
- 下一篇: UVA1601万圣节的早上