1、序列化組件
##簡單使用html
開發咱們的Web API的第一件事是爲咱們的Web API提供一種將代碼片斷實例序列化和反序列化爲諸如json
之類的表示形式的方式。咱們能夠經過聲明與Django forms很是類似的序列化器(serializers)來實現。python
models部分:django
from django.db import models # Create your models here. class Book(models.Model): title=models.CharField(max_length=32) price=models.IntegerField() pub_date=models.DateField() publish=models.ForeignKey("Publish") authors=models.ManyToManyField("Author") def __str__(self): return self.title class Publish(models.Model): name=models.CharField(max_length=32) email=models.EmailField() def __str__(self): return self.name class Author(models.Model): name=models.CharField(max_length=32) age=models.IntegerField() def __str__(self): return self.name
views部分:json
from rest_framework.views import APIView from rest_framework.response import Response from .models import * from django.shortcuts import HttpResponse from django.core import serializers from rest_framework import serializers class BookSerializers(serializers.Serializer): title=serializers.CharField(max_length=32) price=serializers.IntegerField() pub_date=serializers.DateField() publish=serializers.CharField(source="publish.name") #authors=serializers.CharField(source="authors.all") authors=serializers.SerializerMethodField() def get_authors(self,obj): temp=[] for author in obj.authors.all(): temp.append(author.name) return temp class BookViewSet(APIView): def get(self,request,*args,**kwargs): book_list=Book.objects.all() # 序列化方式1: # from django.forms.models import model_to_dict # import json # data=[] # for obj in book_list: # data.append(model_to_dict(obj)) # print(data) # return HttpResponse("ok") # 序列化方式2: # data=serializers.serialize("json",book_list) # return HttpResponse(data) # 序列化方式3: bs=BookSerializers(book_list,many=True) return Response(bs.data)
##ModelSerializerapp
class BookSerializers(serializers.ModelSerializer): class Meta: model=Book fields="__all__" depth=1
##提交post請求框架
def post(self,request,*args,**kwargs): bs=BookSerializers(data=request.data,many=False) if bs.is_valid(): # print(bs.validated_data) bs.save() return Response(bs.data) else: return HttpResponse(bs.errors)
##重寫save中的create方法dom
class BookSerializers(serializers.ModelSerializer): class Meta: model=Book fields="__all__" # exclude = ['authors',] # depth=1 def create(self, validated_data): authors = validated_data.pop('authors') obj = Book.objects.create(**validated_data) obj.authors.add(*authors) return obj
單條數據的get和put請求
class BookDetailViewSet(APIView): def get(self,request,pk): book_obj=Book.objects.filter(pk=pk).first() bs=BookSerializers(book_obj) return Response(bs.data) def put(self,request,pk): book_obj=Book.objects.filter(pk=pk).first() bs=BookSerializers(book_obj,data=request.data) if bs.is_valid(): bs.save() return Response(bs.data) else: return HttpResponse(bs.errors)
##超連接API:Hyperlinkedide
class BookSerializers(serializers.ModelSerializer): publish= serializers.HyperlinkedIdentityField( view_name='publish_detail', lookup_field="publish_id", lookup_url_kwarg="pk") class Meta: model=Book fields="__all__" #depth=1
urls部分:函數
`urlpatterns ``=` `[`` ``url(r``'^books/$'``, views.BookViewSet.as_view(),name``=``"book_list"``),`` ``url(r``'^books/(?P<pk>\d+)$'``, views.BookDetailViewSet.as_view(),name``=``"book_detail"``),`` ``url(r``'^publishers/$'``, views.PublishViewSet.as_view(),name``=``"publish_list"``),`` ``url(r``'^publishers/(?P<pk>\d+)$'``, views.PublishDetailViewSet.as_view(),name``=``"publish_detail"``),``]`
補充
class BookSerializer(serializers.ModelSerializer): dis_chapter = serializers.SerializerMethodField(read_only=True) users = serializers.SerializerMethodField(read_only=True) publishers = serializers.SerializerMethodField(read_only=True) def get_users(self, obj): # obj是當前序列化的book對象 users_query_set = obj.user.all() return [{"id": user_obj.pk, "name": user_obj.name} for user_obj in users_query_set] def get_publishers(self, obj): publisher_obj = obj.publisher return {"id": publisher_obj.pk, "title": publisher_obj.title} def get_dis_chapter(self, obj): return obj.get_chapter_display() class Meta: model = Book # fields = "__all__" # 字段是有序的 fields = ["id", "title","dis_chapter", "pub_time", "publishers", "users","chapter", "user", "publisher"] # exclude = ["user"] # 分別是全部字段 包含某些字段 排除某些字段 read_only_fields = ["id", "dis_chapter", "users", "publishers"] extra_kwargs = {"title": {"validators": [my_validate,]}, "user": {"write_only": True}, "publisher": {"write_only": True}, "chapter": {"write_only": True}} ModelSerializer
驗證
單個字段的驗證post
class BookSerializer(serializers.Serializer): id = serializers.IntegerField(read_only=True) title = serializers.CharField(max_length=32) # 省略了一些字段 跟上面代碼裏同樣的 # 。。。。。 def validate_title(self, value): if "python" not in value.lower(): raise serializers.ValidationError("標題必須含有Python") return value 單個字段的驗證
多個字段的驗證
class BookSerializer(serializers.Serializer): id = serializers.IntegerField(read_only=True) title = serializers.CharField(max_length=32) CHOICES = ((1, "Linux"), (2, "Django"), (3, "Python")) chapter = serializers.ChoiceField(choices=CHOICES, source="get_chapter_display", read_only=True) w_chapter = serializers.IntegerField(write_only=True) pub_time = serializers.DateField() date_added = serializers.DateField(write_only=True) # 新增了一個上架時間字段 # 省略一些字段。。都是在原基礎代碼上增長的 # 。。。。。。 # 對多個字段進行驗證 要求上架日期不能早於出版日期 上架日期要大 def validate(self, attrs): if attrs["pub_time"] > attrs["date_added"]: raise serializers.ValidationError("上架日期不能早於出版日期") return attrs 多個字段的驗證
驗證器
def my_validate(value): if "敏感詞彙" in value.lower: raise serializers.ValidationError("包含敏感詞彙,請從新提交") return value class BookSerializer(serializers.Serializer): id = serializers.IntegerField(read_only=True) title = serializers.CharField(max_length=32, validators=[my_validate]) # 。。。。。。 驗證器 validators
<br>
2、視圖組件之視圖三部曲
##使用混合(mixins)
上一節的視圖部分:
from rest_framework.views import APIView from rest_framework.response import Response from .models import * from django.shortcuts import HttpResponse from django.core import serializers from rest_framework import serializers class BookSerializers(serializers.ModelSerializer): class Meta: model=Book fields="__all__" #depth=1 class PublshSerializers(serializers.ModelSerializer): class Meta: model=Publish fields="__all__" depth=1 class BookViewSet(APIView): def get(self,request,*args,**kwargs): book_list=Book.objects.all() bs=BookSerializers(book_list,many=True,context={'request': request}) return Response(bs.data) def post(self,request,*args,**kwargs): print(request.data) bs=BookSerializers(data=request.data,many=False) if bs.is_valid(): print(bs.validated_data) bs.save() return Response(bs.data) else: return HttpResponse(bs.errors) class BookDetailViewSet(APIView): def get(self,request,pk): book_obj=Book.objects.filter(pk=pk).first() bs=BookSerializers(book_obj,context={'request': request}) return Response(bs.data) def put(self,request,pk): book_obj=Book.objects.filter(pk=pk).first() bs=BookSerializers(book_obj,data=request.data,context={'request': request}) if bs.is_valid(): bs.save() return Response(bs.data) else: return HttpResponse(bs.errors) class PublishViewSet(APIView): def get(self,request,*args,**kwargs): publish_list=Publish.objects.all() bs=PublshSerializers(publish_list,many=True,context={'request': request}) return Response(bs.data) def post(self,request,*args,**kwargs): bs=PublshSerializers(data=request.data,many=False) if bs.is_valid(): # print(bs.validated_data) bs.save() return Response(bs.data) else: return HttpResponse(bs.errors) class PublishDetailViewSet(APIView): def get(self,request,pk): publish_obj=Publish.objects.filter(pk=pk).first() bs=PublshSerializers(publish_obj,context={'request': request}) return Response(bs.data) def put(self,request,pk): publish_obj=Publish.objects.filter(pk=pk).first() bs=PublshSerializers(publish_obj,data=request.data,context={'request': request}) if bs.is_valid(): bs.save() return Response(bs.data) else: return HttpResponse(bs.errors)
##mixin類編寫視圖
from rest_framework import mixins from rest_framework import generics class BookViewSet(mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView): queryset = Book.objects.all() serializer_class = BookSerializers def get(self, request, *args, **kwargs): return self.list(request, *args, **kwargs) def post(self, request, *args, **kwargs): return self.create(request, *args, **kwargs) class BookDetailViewSet(mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, generics.GenericAPIView): queryset = Book.objects.all() serializer_class = BookSerializers def get(self, request, *args, **kwargs): return self.retrieve(request, *args, **kwargs) def put(self, request, *args, **kwargs): return self.update(request, *args, **kwargs) def delete(self, request, *args, **kwargs): return self.destroy(request, *args, **kwargs)
##使用通用的基於類的視圖
經過使用mixin類,咱們使用更少的代碼重寫了這些視圖,但咱們還能夠再進一步。REST框架提供了一組已經混合好(mixed-in)的通用視圖,咱們可使用它來簡化咱們的views.py
模塊。
from rest_framework import mixins from rest_framework import generics class BookViewSet(generics.ListCreateAPIView): queryset = Book.objects.all() serializer_class = BookSerializers class BookDetailViewSet(generics.RetrieveUpdateDestroyAPIView): queryset = Book.objects.all() serializer_class = BookSerializers class PublishViewSet(generics.ListCreateAPIView): queryset = Publish.objects.all() serializer_class = PublshSerializers class PublishDetailViewSet(generics.RetrieveUpdateDestroyAPIView): queryset = Publish.objects.all() serializer_class = PublshSerializers
##viewsets.ModelViewSet
urls.py:
url(r'^books/$', views.BookViewSet.as_view({"get":"list","post":"create"}),name="book_list"), url(r'^books/(?P<pk>\d+)$', views.BookViewSet.as_view({ 'get': 'retrieve', 'put': 'update', 'patch': 'partial_update', 'delete': 'destroy' }),name="book_detail"),
views.py:
class BookViewSet(viewsets.ModelViewSet): queryset = Book.objects.all() serializer_class = BookSerializers
路由
咱們上面的路由傳參寫的特別多~~框架也幫咱們封裝好了~
from .views import BookView from rest_framework.routers import DefaultRouter router = DefaultRouter() router.register(r"book", BookView) urlpatterns = [ # url(r'^book$', BookView.as_view()), # url(r'^book/(?P<id>\d+)$', BookEditView.as_view()), # url(r'^book$', BookView.as_view({"get": "list", "post": "create"})), # url(r'^book/(?P<pk>\d+)$', BookView.as_view({"get": "retrieve", "patch": "update", "delete": "destroy"})), ] urlpatterns += router.urls
咱們能夠看到經過框架咱們能夠把路由視圖都變的很是簡單
可是須要自定製的時候仍是須要咱們本身用APIView寫當不須要那麼多路由的時候~也不要用這種路由註冊
總之一切按照業務須要去用
<br>
3、認證組件
##局部視圖認證
在app01.service.auth.py:
class Authentication(BaseAuthentication): def authenticate(self,request): token=request._request.GET.get("token") token_obj=UserToken.objects.filter(token=token).first() if not token_obj: raise exceptions.AuthenticationFailed("驗證失敗!") return (token_obj.user,token_obj)
在views.py:
def get_random_str(user): import hashlib,time ctime=str(time.time()) md5=hashlib.md5(bytes(user,encoding="utf8")) md5.update(bytes(ctime,encoding="utf8")) return md5.hexdigest() from app01.service.auth import * from django.http import JsonResponse class LoginViewSet(APIView): authentication_classes = [Authentication,] def post(self,request,*args,**kwargs): res={"code":1000,"msg":None} try: user=request._request.POST.get("user") pwd=request._request.POST.get("pwd") user_obj=UserInfo.objects.filter(user=user,pwd=pwd).first() print(user,pwd,user_obj) if not user_obj: res["code"]=1001 res["msg"]="用戶名或者密碼錯誤" else: token=get_random_str(user) UserToken.objects.update_or_create(user=user_obj,defaults={"token":token}) res["token"]=token except Exception as e: res["code"]=1002 res["msg"]=e return JsonResponse(res,json_dumps_params={"ensure_ascii":False})
##全局視圖認證組件
settings.py配置以下:
`REST_FRAMEWORK``=``{`` ``"DEFAULT_AUTHENTICATION_CLASSES"``:[``"app01.service.auth.Authentication"``,]``}`
4、權限組件
##局部視圖權限
在app01.service.permissions.py中:
from rest_framework.permissions import BasePermission class SVIPPermission(BasePermission): message="SVIP才能訪問!" def has_permission(self, request, view): if request.user.user_type==3: return True return False
在views.py:
from app01.service.permissions import * class BookViewSet(generics.ListCreateAPIView): permission_classes = [SVIPPermission,] queryset = Book.objects.all() serializer_class = BookSerializers
##全局視圖權限
settings.py配置以下:
`REST_FRAMEWORK``=``{`` ``"DEFAULT_AUTHENTICATION_CLASSES"``:[``"app01.service.auth.Authentication"``,],`` ``"DEFAULT_PERMISSION_CLASSES"``:[``"app01.service.permissions.SVIPPermission"``,]``}`
5、throttle(訪問頻率)組件
##局部視圖throttle
在app01.service.throttles.py中:
from rest_framework.throttling import BaseThrottle VISIT_RECORD={} class VisitThrottle(BaseThrottle): def __init__(self): self.history=None def allow_request(self,request,view): remote_addr = request.META.get('REMOTE_ADDR') print(remote_addr) import time ctime=time.time() if remote_addr not in VISIT_RECORD: VISIT_RECORD[remote_addr]=[ctime,] return True history=VISIT_RECORD.get(remote_addr) self.history=history while history and history[-1]<ctime-60: history.pop() if len(history)<3: history.insert(0,ctime) return True else: return False def wait(self): import time ctime=time.time() return 60-(ctime-self.history[-1])
在views.py中:
from app01.service.throttles import * class BookViewSet(generics.ListCreateAPIView): throttle_classes = [VisitThrottle,] queryset = Book.objects.all() serializer_class = BookSerializers
##全局視圖throttle
REST_FRAMEWORK={ "DEFAULT_AUTHENTICATION_CLASSES":["app01.service.auth.Authentication",], "DEFAULT_PERMISSION_CLASSES":["app01.service.permissions.SVIPPermission",], "DEFAULT_THROTTLE_CLASSES":["app01.service.throttles.VisitThrottle",] }
##內置throttle類
在app01.service.throttles.py修改成:
class VisitThrottle(SimpleRateThrottle): scope="visit_rate" def get_cache_key(self, request, view): return self.get_ident(request)
settings.py設置:
REST_FRAMEWORK={ "DEFAULT_AUTHENTICATION_CLASSES":["app01.service.auth.Authentication",], "DEFAULT_PERMISSION_CLASSES":["app01.service.permissions.SVIPPermission",], "DEFAULT_THROTTLE_CLASSES":["app01.service.throttles.VisitThrottle",], "DEFAULT_THROTTLE_RATES":{ "visit_rate":"5/m", } }
6、解析器
##request類
django的request類和rest-framework的request類的源碼解析
##局部視圖
from rest_framework.parsers import JSONParser,FormParser class PublishViewSet(generics.ListCreateAPIView): parser_classes = [FormParser,JSONParser] queryset = Publish.objects.all() serializer_class = PublshSerializers def post(self, request, *args, **kwargs): print("request.data",request.data) return self.create(request, *args, **kwargs)
##全局視圖
REST_FRAMEWORK={ "DEFAULT_AUTHENTICATION_CLASSES":["app01.service.auth.Authentication",], "DEFAULT_PERMISSION_CLASSES":["app01.service.permissions.SVIPPermission",], "DEFAULT_THROTTLE_CLASSES":["app01.service.throttles.VisitThrottle",], "DEFAULT_THROTTLE_RATES":{ "visit_rate":"5/m", }, "DEFAULT_PARSER_CLASSES":['rest_framework.parsers.FormParser',] }
7、分頁
##簡單分頁
from rest_framework.pagination import PageNumberPagination,LimitOffsetPagination class PNPagination(PageNumberPagination): page_size = 1 page_query_param = 'page' page_size_query_param = "size" max_page_size = 5 class BookViewSet(viewsets.ModelViewSet): queryset = Book.objects.all() serializer_class = BookSerializers def list(self,request,*args,**kwargs): book_list=Book.objects.all() pp=LimitOffsetPagination() pager_books=pp.paginate_queryset(queryset=book_list,request=request,view=self) print(pager_books) bs=BookSerializers(pager_books,many=True) #return Response(bs.data) return pp.get_paginated_response(bs.data)
##偏移分頁
from rest_framework.pagination import LimitOffsetPagination
8、版本控制
版本控制是作什麼用的, 咱們爲何要用
首先咱們要知道咱們的版本是幹嗎用的呢你們都知道咱們開發項目是有多個版本的
隨着咱們項目的更新~版本就愈來愈多咱們不可能新的版本出了~之前舊的版本就不進行維護了~
那咱們就須要對版本進行控制這個DRF也給咱們提供了一些封裝好的版本控制方法
版本控制怎麼用
以前咱們學視圖的時候知道APIView,也知道APIView返回View中的view函數,而後調用的dispatch方法~
那咱們如今看下dispatch方法看下它都作了什麼
執行self.initial方法以前是各類賦值,包括request的從新封裝賦值,下面是路由的分發,那咱們看下這個方法都作了什麼~~
咱們能夠看到,咱們的version版本信息賦值給了 request.version 版本控制方案賦值給了 request.versioning_scheme~~
其實這個版本控制方案~就是咱們配置的版本控制的類~~
也就是說,APIView經過這個方法初始化本身提供的組件~~
咱們接下來看看框架提供了哪些版本的控制方法~~在rest_framework.versioning裏
框架一共給咱們提供了這幾個版本控制的方法咱們在這裏只演示一個由於基本配置都是同樣的~~
詳細用法
咱們看下放在URL上攜帶版本信息怎麼配置~~
-
setting配置
REST_FRAMEWORK = { # 默認使用的版本控制類 'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning', # 容許的版本 'ALLOWED_VERSIONS': ['v1', 'v2'], # 版本使用的參數名稱 'VERSION_PARAM': 'version', # 默認使用的版本 'DEFAULT_VERSION': 'v1', }
-
urls.py
urlpatterns = [ url(r"^versions", MyView.as_view()), url(r"^(?P<version>[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("根本就匹配不到這個路由")
其餘的版本控制的類,配置方法都差很少~~這裏就不一一例舉了 詳細請見:https://www.cnblogs.com/GGGG-XXXX/p/9564651.html
<br>
<br>
<br>