
restful協議 ---- 一切皆是資源,操作只是請求方式 ----book表增刪改查 /books/ books /books/add/ addbook /books/(\d+)/change/ changebook /books/(\d+)/delete/ delbook ----book表增刪改查 /books/ -----get books ----- 返回當前所有數據 /books/ -----post books ----- 返回提交數據 /books/(\d+)-----get bookdetail ----- 返回當前查看的單條數據 /books/(\d+)-----put bookdetail ----- 返回更新數據 /books/(\d+)-----delete bookdetail ----- 返回空 class Books(View): def get(self,request): pass # 查看所有書籍 def post(self,request): pass # 添加書籍 class BooksDetail(View): def get(self,request,id): pass # 查看具體書籍 def put(self,request,id): pass # 更新某本書籍 def delete(self,request,id): pass # 刪除某本書籍 restframework(Django) ----針對數據:json (1)Django的原生request:(django默認的request中沒有對json數據進行解析) 瀏覽器 ------------- 服務器 "GET url?a=1&b=2 http/1.1\r\user_agent:Google\r\ncontentType:urlencoded\r\n\r\n" "POST url http/1.1\r\user_agent:Google\r\ncontentType:urlencoded\r\n\r\na=1&b=2" request.body: a=1&b=2 request.POST: if contentType:urlencoded: a=1&b=2----->{"a":1,"b":2} (2)restframework 下的APIView: (3) class PublishSerializers(serializers.Serializer): name=serializers.CharField() email=serializers.CharField() PublishSerializers(queryset,many=true) PublishSerializers(model_obj) 總結: 1 reuqest類----源碼 2 restframework 下的APIView--源碼 url(r'^books/$', views.BookView.as_view(),name="books")# View下的view books/一旦被訪問: view(request) ------APIView: dispatch() 3 def dispatch(): 構建request對象 self.request=Request(request) self.request._request self.request.GET # get self.request.data # POST PUT 分發----if get請求: 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) # self.get(request, *args, **kwargs) return response 4 序列化類 # from django.core import serializers # ret=serializers.serialize("json",publish_list) restframework下的序列類 BookModelSerializers 將queryset或者model對象序列成一json數據 bs=BookModelSerializers(book_list,many=True,context={'request': request}) bs=BookModelSerializers(book,context={'request': request}) 還可以做校驗數據,json-------》queryset/model-->記錄 bs=BookModelSerializers(data=request.data) if bs.is_valid(): print(bs.validated_data) bs.save() # 重寫create方法 5 操作數據: 以Book表為例 class BookView(APIView): # 查看所有書籍 def get(self,request): book_list=Book.objects.all() bs=BookModelSerializers(book_list,many=True,context={'request': request}) return Response(bs.data) # 添加一本書籍 def post(self,request): # post請求的數據 bs=BookModelSerializers(data=request.data) if bs.is_valid(): print(bs.validated_data) bs.save()# create方法 return Response(bs.data) else: return Response(bs.errors) class BookDetailView(APIView): # 查看一本書籍 def get(self,request,id): book=Book.objects.filter(pk=id).first() bs=BookModelSerializers(book,context={'request': request}) return Response(bs.data) # 更新一本書籍 def put(self,request,id): book=Book.objects.filter(pk=id).first() bs=BookModelSerializers(book,data=request.data) if bs.is_valid(): bs.save() return Response(bs.data) else: return Response(bs.errors) # 刪除某一本書籍 def delete(self,request,id): Book.objects.filter(pk=id).delete() return Response()
快速實例:Quickstart
使用restframework先下載:cmd->pip3 install djangorestframework
使用rest_framework前先在settings中進行app的注冊
一、APIView源碼解析
from rest_framework.views import APIView class PublishView(APIView): def get(self,request): pass
def post(self,request): pass
urls.py
url(r'^publishes/$', views.PublishView.as_view(),name="publish"),
當用戶訪問publishes時執行as_view()方法,但PublishView類下面沒有as_view()方法,去找父類APIView
#rest_framework\views.py class APIView(View): #繼承View # …… @classmethod def as_view(cls, **initkwargs): # …… view = super(APIView, cls).as_view(**initkwargs) #執行其父類View中的as_view()方法拿到返回結果(view) view.cls = cls view.initkwargs = initkwargs return csrf_exempt(view) #返回函數名view,其實拿到的是父類View中as_view()方法返回的view #一旦用戶訪問publishes時就執行父類View中的view方法,父類View中view()執行結束之后返回了一個dispatch方法 #return self.dispatch(request, *args, **kwargs) #dispatch()調用的先后順序:應該最開始self是PublishView,但其下沒有dispatch方法,然后找其父類APIView, # 發現下面定義了dispatch方法,此次就不執行APIView父類View中的dispatch方法 #經過以上分析用戶訪問publishes最終執行的是dispatch方法(首先要搞清楚是哪個類下的dispatch方法) # …… def initialize_request(self, request, *args, **kwargs): parser_context = self.get_parser_context(request) return Request( request, #舊的request parsers=self.get_parsers(), authenticators=self.get_authenticators(), negotiator=self.get_content_negotiator(), parser_context=parser_context ) # …… def dispatch(self, request, *args, **kwargs): #resr_framework所有的接口都封裝在dispatch方法中#構建一個新的request request = self.initialize_request(request, *args, **kwargs) self.request = request #這句話之后下面所用的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 # handler方法的執行其實就是get/post等方法的執行,傳進去的request是新的request #也就是說視圖get(self,request)中的request是新的request response = handler(request, *args, **kwargs) # …… #request.py class Request(object): def __init__(self, request, parsers=None, authenticators=None, negotiator=None, parser_context=None): # …… self._request = request #self是當前Request對象,其實就是新的request對象 #新的request對象下面有個實例變量_request,self._request的結果就是舊的request # …… @property def data(self): #先記住一點:request.data拿到的是所有請求過來的數據 if not _hasattr(self, '_full_data'): self._load_data_and_files() return self._full_data #_full_data默認為空 def _load_data_and_files(self): if not _hasattr(self, '_data'): self._data, self._files = self._parse() if self._files: self._full_data = self._data.copy() #如果是_files,對_full_data進行賦值 self._full_data.update(self._files) else: self._full_data = self._data # …… def _parse(self): #解析器 不同的數據用不同的解析器解析 # …… try: return (parsed.data, parsed.files) #最終返回的值是解析之后的源數據 except AttributeError: empty_files = MultiValueDict() return (parsed, empty_files)
分析源碼之后我們需要知道三點:
a、request是新的request,通過 self._request = request可以調用舊的request
b、request.data可以拿到POST、PUT等請求(不包括GET請求)過來的數據
c、request.GET可以拿到GET請求過來的數據
d、APIView是在CBV基礎上做的,基於View又擴展了一些功能
二、序列化
創建一個序列化類
1、簡單使用
開發我們的Web API的第一件事是為我們的Web API提供一種將代碼片段實例序列化和反序列化為諸如json
之類的表示形式的方式。我們可以通過聲明與Django forms非常相似的序列化器(serializers)來實現。
model_to_dict:把model 對象轉換為一個字典
>>> from app01 import models >>> obj=models.Publish.objects.filter(pk=1).first() >>> obj <Publish: 蘋果出版社> >>> from django.forms.models import model_to_dict >>> model_to_dict(obj) {'id': 1, 'name': '蘋果出版社', 'email': '123@qq.com'}
models部分:
from django.db import models 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部分:
from rest_framework.views import APIView from rest_framework.response import Response from .models import * from rest_framework import serializers class BookSerializer(serializers.Serializer): title = serializers.CharField(max_length=32) price = serializers.IntegerField() pub_date = serializers.DateField() publish = serializers.CharField(source="publish.name") # 一對多 authors = serializers.SerializerMethodField() # 多對多 def get_authors(self, obj): temp = [] for author in obj.authors.all(): temp.append(author.name) return temp """ 序列化BookSerializer(book_list,many=True) temp=[] for obj in book_list: temp.append({ "title":obj.title, "price":obj.price, "pub_date":obj.pub_date, #"publish":str(obj.publish), obj.publish.name # "authors":obj.authors.all, "authors":get_authors(obj), }) """ class BookViewSet(APIView): #這里是序列化的方式示例:真正的get請求在下面 def get(self,request,*args,**kwargs): book_list = Book.objects.all() # 序列化方式1:list強轉為列表,列表里面放字典 # publish_list=list(Book.objects.all().values("title","price")) # 序列化方式2: # from django.forms.models import model_to_dict # data=[] # for obj in book_list: # data.append(model_to_dict(obj)) # print(data) # return HttpResponse("ok") # 序列化方式3: # data=serializers.serialize("json",book_list) # return HttpResponse(data) # 序列化方式4: bs=BookSerializer(book_list,many=True) return Response(bs.data)
2、ModelSerializer
只需要寫下面幾步代碼會自動幫我們重建BookSerializer那一堆代碼
#將一個queryset或者model對象序列化為json數據
class BookModelSerializers(serializers.ModelSerializer): class Meta: model = Book fields = "__all__" # depth=1 # 上面展示的是第一幅圖:默認展示一對多和多對多的主鍵值,加了這個之后展示給我們的是第二幅圖 #上面在轉換一對多或多對多的時候會展示多的一方的主鍵值,我們也可自定義顯示方法,定義了下面幾行代碼后get請求顯示方式如第三幅圖 publish=serializers.CharField(source="publish.name") #一對多 authors=serializers.SerializerMethodField() #多對多 def get_authors(self, obj): temp = [] for author in obj.authors.all(): temp.append(author.name) return temp
3、get請求和post請求
url(r'^books/$', views.BookView.as_view(),name="books"),
上面展示的畫面就是利用下面的get請求獲取的
class BookView(APIView): def get(self,request): book_list=Book.objects.all() # bs=BookSerializer(book_list,many=True) #利用自己定義的BookSerializer顯示 bs=BookModelSerializers(book_list,many=True) #利用BookMdelSerializers展現的數據,不需要我們寫BookSerializer那一堆代碼,這個是序列化queryset # return HttpResponse(bs.data) #返回的是下面的字符串 #OrderedDict([('title', '三體'), ('price', 233), ('pub_date', None)]) # OrderedDict([('title', '追風箏的人'), ('price', 333), ('pub_date', None)]) return Response(bs.data) #顯示的是上面的畫面,可讀性更強 def post(self,request): # post請求的數據 bs=BookModelSerializers(data=request.data) #序列化數據----->queryset--->數據記錄 if bs.is_valid(): print(bs.validated_data) bs.save()# create方法:把生成的數據保存到數據庫中 return Response(bs.data) #返回提交數據 else: return Response(bs.errors) #提交信息有錯返回錯誤信息
4、重寫save中的create方法
post請求提交的數據:
當我們發post請求上面post中的save方法走的是ModelSerializer里面的create方法,但這個方法不支持我們自定制顯示的玩法(不支持source="publish.pk"),
所以就需要我們重寫save中的create方法 ,post請求時就會走我們自己定義的create方法(如果我們不加自定制的publish字段就不需要自定制create方法)
有個疑點:
class BookModelSerializers(serializers.ModelSerializer): class Meta: model = Book fields = "__all__" # depth=1 publish=serializers.CharField(source="publish.pk") #一對多
def create(self, validated_data): print("validated_data->",validated_data) #validated_data-> {'publish': {'pk': '1'}, 'title': 'go', 'price': 100,'pub_date': datetime.date(2012, 12, 12), 'authors': [<Author: alex>, <Author: egon>]} book = Book.objects.create(title=validated_data["title"],price=validated_data["price"],pub_date=validated_data["pub_date"],publish_id=validated_data["publish"]["pk"]) book.authors.add(*validated_data["authors"]) return book
post請求成功后返回的畫面
5、單條數據的get、put和delete請求
book表:
url(r'^books/(\d+)/$', views.BookDetailView.as_view(),name="detailbook"),
class BookModelSerializers(serializers.ModelSerializer): class Meta: model = Book fields = "__all__" class BookDetailView(APIView): #單條數據的查看 def get(self,request,pk): book=Book.objects.filter(pk=pk).first() bs=BookModelSerializers(book) #序列化model對象 return Response(bs.data) #單條數據的更新 def put(self,request,pk): book=Book.objects.filter(pk=pk).first() bs=BookModelSerializers(book,data=request.data) if bs.is_valid():#校驗put請求提交的數據 bs.save() #update操作 return Response(bs.data) else: return Response(bs.errors) # 單條數據的刪除 def delete(self,request,pk): Book.objects.filter(pk=id).delete() return Response() #刪除后返回空
6、超鏈接API:Hyperlinked
url(r'^publishes/(?P<pk>\d+)/$', views.PublishDetailView.as_view(),name="detailpublish"),
class BookModelSerializers(serializers.ModelSerializer): class Meta: model = Book fields = "__all__" # publish=serializers.CharField(source="publish.pk") publish=serializers.HyperlinkedIdentityField( view_name="detailpublish", #url的別名 lookup_field="publish_id", #取當前循環字段關聯publish的id值 lookup_url_kwarg="pk" #把上面找到的id值放到pk組中 ) # publishes/(?P<pk>\d+)/$
需要注意的是在使用Hyperlinked這個方法后,上面的get、post、put中的BookModelSerializers中都需要加context={'request': request}