Django REST framework官方文檔:點擊 中文文檔:點擊前端
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'app01', 'rest_framework', ]
作先後端分離的項目,先後端交互通常都選擇JSON數據格式,JSON是一個輕量級的數據交互格式。後端給前端數據的時候都要轉成json格式,那就須要對從數據庫拿到的數據進行序列化。python
要用DRF的序列化,就要遵循人家框架的一些標準git
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
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("")
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,]}}
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__'
#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/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)
#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
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"})), ]
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
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
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寫,當不須要那麼多路由的時候,也不要用這種路由註冊.
隨着項目的更新,版本就愈來愈多,不可能新的版本出了,之前舊的版本就不進行維護了,就須要對版本進行控制了github
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)
REST_FRAMEWORK = { # 默認使用的版本控制類 'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning', # 容許的版本 'ALLOWED_VERSIONS': ['v1', 'v2'], # 版本使用的參數名稱 'VERSION_PARAM': 'version', # 默認使用的版本 'DEFAULT_VERSION': 'v1', }
urlpatterns = [ url(r"^versions", MyView.as_view()), url(r"^(?P[v1|v2]+)/test01", TestView.as_view()), ]
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("根本就匹配不到這個路由")
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" }
from django.urls import path, include from .views import DemoView urlpatterns = [ path(r"", DemoView.as_view()), ]
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
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("不存在的版本")
每次給服務器發請求,因爲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 = 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]
# 先在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()
from django.urls import path from .views import DemoView, LoginView, TestView urlpatterns = [ path(r"login", LoginView.as_view()), path(r"test", TestView.as_view()), ]
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("認證測試")
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
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", ] #全局配置 }
對某件事情決策的範圍和程度叫作權限django
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) )
# 先在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)
from django.urls import path from .views import DemoView, LoginView, TestView urlpatterns = [ path(r"login", LoginView.as_view()), path(r"test", TestView.as_view()), ]
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("認證測試")
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
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"] }
開放平臺的API接口調用須要限制其頻率,以節約服務器資源和避免惡意的頻繁調用。json
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次, 若是大於就限流,不然放行,並把時間放入列表的最前面。
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("認證測試")
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
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定義的值, } }
from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination, CursorPagination
REST_FRAMEWORK = { 'PAGE_SIZE': 2 }
http://127.0.0.1:8000/book?page=2&size=1
http://127.0.0.1:8000/book?offset=2&limit=1
http://127.0.0.1:8000/book?page=2&size=1
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"
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)
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解析器的測試~~")
渲染器就是友好的展現數據,咱們在瀏覽器中展現的DRF測試的那個頁面就是經過瀏覽器的渲染器來作到的,固然咱們能夠展現Json數據類型後端
DEFAULTS = { # Base API policies 'DEFAULT_RENDERER_CLASSES': ( 'rest_framework.renderers.JSONRenderer', 'rest_framework.renderers.BrowsableAPIRenderer', ),