Quickstarthtml
http://www.cnblogs.com/yuanchenqi/articles/8719520.html前端
restful協議 ---- 一切皆是資源,操做只是請求方式 ----book表增刪改查 /books/ books /books/add/ addbook /books/(\d+)/change/ changebook /books/(\d+)/delete/ delbook ----book表增刪改查 url裏面不能出現動詞!! /books/ -----get books ----- 返回當前全部數據 /books/ -----post books ----- 返回提交數據 /books/(\d+)-----get bookdetail ----- 返回當前查看的單條數據 /books/(\d+)-----put bookdetail ----- 返回更新數據 /books/(\d+)-----delete bookdetail ----- 返回空 http://www.cnblogs.com/yuanchenqi/articles/8719520.html http://www.django-rest-framework.org/tutorial/quickstart/#quickstart 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 # 刪除某本書籍 http://www.django-rest-framework.org/tutorial/quickstart/#quickstart restframework(Django) app pip install django pip install djangorestframework ----針對數據:json (1)Django的原生request: 瀏覽器 ------------- 服務器 "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() restframework 1 APIView 2 序列組件 3 視圖、 4 組件(認證權限頻率) 5 數據解析器 6 分頁和Response
models.py:node
from django.db import models # Create your models here. class User(models.Model): name = models.CharField(max_length=32) pwd = models.CharField(max_length=32) type_choice = ((1, "普通用戶"), (2, "VIP"), (3, "SVIP")) user_type = models.IntegerField(choices=type_choice, default=1) class Token(models.Model): user = models.OneToOneField("User", on_delete=models.CASCADE) token = models.CharField(max_length=128) def __str__(self): return self.token class Book(models.Model): title = models.CharField(max_length=32) price = models.IntegerField() pub_date = models.DateField() publish = models.ForeignKey("Publish", on_delete=models.CASCADE) 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
urls.py:python
from django.contrib import admin from django.urls import path from app01 import views urlpatterns = [ path('admin/', admin.site.urls), path('publishes/', views.PublishView.as_view()) ]
pip install djangorestframework
INSTALLED_APPS = ( ... 'rest_framework', )
class PublishView(View): def get(self, request): print('get', request.GET) return HttpResponse('123') def post(self, request): print('post', request.POST) print('body', request.body) print(type(request)) return HttpResponse('POST')
訪問地址http://127.0.0.1:8000/publishes/?a=3&c=7 ,打印獲得get請求數據:<QueryDict: {'a': ['3'], 'c': ['7']}>。get請求相似形式以下:正則表達式
"GET url?a=1&b=2 http/1.1\r\user_agent:Google\r\ncontentType:urlencoded\r\n\r\n"
在Postman提交post請求,打印結果以下所示:數據庫
post <QueryDict: {'a': ['14'], 'b': ['9']}> body b'a=14&b=9'
body放的是原數據,即報文,沒有作任何解析。django
post會幫忙作contentType是不是urlencoded的判斷,若是是的會幫忙將 a=1&b=2 轉化爲 {"a":1,"b":2} 。post請求相似形式以下:json
"POST url http/1.1\r\user_agent:Google\r\ncontentType:urlencoded\r\n\r\na=1&b=2"
打印獲得<class 'django.core.handlers.wsgi.WSGIRequest'>。能夠引入WSGIRequest來查看源碼:數組
from django.core.handlers.wsgi import WSGIRequest
關於post源碼以下所示:瀏覽器
class WSGIRequest(HttpRequest): def _get_post(self): if not hasattr(self, '_post'): self._load_post_and_files() return self._post def _set_post(self, post): self._post = post POST = property(_get_post, _set_post)
處理請求的多種可能:
def _load_post_and_files(self): """Populate self._post and self._files if the content-type is a form type""" if self.method != 'POST': self._post, self._files = QueryDict(encoding=self._encoding), MultiValueDict() return if self._read_started and not hasattr(self, '_body'): self._mark_post_parse_error() return if self.content_type == 'multipart/form-data':... elif self.content_type == 'application/x-www-form-urlencoded': self._post, self._files = QueryDict(self.body, encoding=self._encoding), MultiValueDict() else: self._post, self._files = QueryDict(encoding=self._encoding), MultiValueDict()
若是content_type是urlencoded,post裏面纔會有QueryDict,將body裏的內容作成字典的形式。
若是content_type不是form-data也不是urlencoded,則QueryDict裏沒有值,一個空的字典。
from rest_framework.views import APIView
進入rest_framework/view.py中查看APIView的源碼:
class APIView(View): @classmethod def as_view(cls, **initkwargs): if isinstance(getattr(cls, 'queryset', None), models.query.QuerySet):... view = super(APIView, cls).as_view(**initkwargs) # 執行父類View裏的as_view方法,返回view return csrf_exempt(view) # 返回的依然是View中的view方法
所以訪問publishes/地址後,執行views.PublishView.as_view(),返回的是view方法。VIew.view方法執行返回dispatch(),在這裏優先執行子類的dispatch,所以APIView.dispatch(request)執行。
def initialize_request(self, request, *args, **kwargs): parser_context = self.get_parser_context(request) # 實例化一個Request類的對象 return 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): """ `.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 # 用initialize將舊request構建爲一個新的request request = self.initialize_request(request, *args, **kwargs)
能夠看到在dispatch中,使用initialize方法將舊的request構建爲了一個新的request。在initialize_request中,返回實例化的Request類對象。
class Request(object): def __init__(self, request, parsers=None, authenticators=None, negotiator=None, parser_context=None): self._request = request self._data = Empty self._files = Empty self._full_data = Empty # 默認爲空 @property def data(self): if not _hasattr(self, '_full_data'): self._load_data_and_files() return self._full_data def _load_data_and_files(self): """ Parses the request content into `self.data`. """ if not _hasattr(self, '_data'): self._data, self._files = self._parse() if self._files: self._full_data = self._data.copy() self._full_data.update(self._files) else: self._full_data = self._data # if a form media type, copy data & files refs to the underlying # http request so that closable objects are handled appropriately. if is_form_media_type(self.content_type): self._request._post = self.POST self._request._files = self.FILES def _parse(self): """ Parse the request content, returning a two-tuple of (data, files) May raise an `UnsupportedMediaType`, or `ParseError` exception. """ media_type = self.content_type try: stream = self.stream except RawPostDataException: if not hasattr(self._request, '_post'): raise # If request.POST has been accessed in middleware, and a method='POST' # request was made with 'multipart/form-data', then the request stream # will already have been exhausted. if self._supports_form_parsing(): return (self._request.POST, self._request.FILES) stream = None if stream is None or media_type is None: if media_type and is_form_media_type(media_type): empty_data = QueryDict('', encoding=self._request._encoding) else: empty_data = {} empty_files = MultiValueDict() return (empty_data, empty_files) parser = self.negotiator.select_parser(self, self.parsers) if not parser: raise exceptions.UnsupportedMediaType(media_type) try: parsed = parser.parse(stream, media_type, self.parser_context) except Exception: # If we get an exception during parsing, fill in empty data and # re-raise. Ensures we don't simply repeat the error when # attempting to render the browsable renderer response, or when # logging the request or similar. self._data = QueryDict('', encoding=self._request._encoding) self._files = MultiValueDict() self._full_data = self._data raise # Parser classes may return the raw data, or a # DataAndFiles object. Unpack the result as required. try: return (parsed.data, parsed.files) except AttributeError: empty_files = MultiValueDict() return (parsed, empty_files)
能夠看到最終是經過_parse方法,進行解析器解析。
class PublishView(APIView): def get(self, request): print('request.data', request.data) print('request.data type', type(request.data)) print('request._requet.GET', request._request.GET) print('request.GET', request.GET) return HttpResponse('123') def post(self, request): # 原生request支持的操做 # print('post', request.POST) # print('body', request.body) # print(type(request)) from django.core.handlers.wsgi import WSGIRequest # 新的request支持的操做 print("request.data", request.data) print("request.data type", type(request.data)) return HttpResponse('POST')
控制檯輸出以下:
request.data {'name': 'yuan', 'email': '123@qq.com'} request.data type <class 'dict'>
控制檯輸出以下:
request.data <QueryDict: {'a': ['14'], 'b': ['9']}> request.data type <class 'django.http.request.QueryDict'>
控制檯輸出以下:
request.data <QueryDict: {}> request.data type <class 'django.http.request.QueryDict'> request._requet.GET <QueryDict: {'a': ['3'], 'c': ['7']}> request.GET <QueryDict: {'a': ['3'], 'c': ['7']}>
說明只處理了POST請求的request.data,get請求獲取數據必須經過request._request.GET,rest爲了方便用戶使用,也爲request.GET作了從新賦值,所以也可使用requet.GET獲取數據。
request.body只放請求體裏的數據,get請求沒有請求體,所以輸出的是<QueryDict: {}>。
python中的json包主要提供了dump,load來實現dict與字符串之間的序列化與反序列化。
可是json包不能序列化django的models裏面的對象實例。
from django.shortcuts import render, HttpResponse from django.views import View from .models import Publish import json class PublishView(View): def get(self, request): # QuerySet對象不能進行json序列化 # 方式1:values(*field):調用者是queryset對象,運行後獲得的並非一系列model的實例化對象,而是一個可迭代的字典序列 # 再使用list()方法強轉爲列表,組成列表裏面放字典的數據結構 publish_list = list(Publish.objects.all().values("name", "email")) return HttpResponse(json.dumps(publish_list)) def post(self, request): pass
注意:
調用者是queryset對象,運行後獲得的並非一系列model的實例化對象,而是一個可迭代的字典序列:
<QuerySet [{'name': '橘子出版社', 'email': '456@qq.com'}, {'name': '蘋果出版社', 'email': '123@qq.com'}]>
將序列強行轉化爲數組:
[{'name': '橘子出版社', 'email': '456@qq.com'}, {'name': '蘋果出版社', 'email': '123@qq.com'}]
json.dumps 用於將 Python 對象編碼成 JSON 字符串。如下是python 原始類型向 json 類型的轉化對照表:
from django.views import View from .models import Publish import json class PublishView(View): def get(self, request): # QuerySet對象不能進行json序列化 # 方式2: publish_list = Publish.objects.all() temp = [] for obj in publish_list: temp.append({ "name": obj.name, "name": obj.email }) print(temp) # [{'name': '456@qq.com'}, {'name': '123@qq.com'}] return HttpResponse(json.dumps(temp)) def post(self, request): pass
這樣寫的問題是有多少字段就要加多少個字段,並且若是不知道是哪張表或者有哪些字段,就沒法構建數據。
model_to_dict是用於將model對象轉換爲字典的方法。
from django.views import View from .models import Publish import json class PublishView(View): def get(self, request): # QuerySet對象不能進行json序列化 # 方式2: from django.forms.models import model_to_dict publish_list = Publish.objects.all() temp = [] for obj in publish_list: temp.append(model_to_dict(obj)) print(temp) # [{'name': '456@qq.com'}, {'name': '123@qq.com'}] return HttpResponse(json.dumps(temp))
在pycharm的python console測試:
>>>from django.forms.models import model_to_dict >>>from app01 import models >>>print(models) <module 'app01.models' from '/Users/hqs/PycharmProjects/restDemo/app01/models.py'> >>>obj = models.Publish.objects.all() >>>print(obj) <QuerySet [<Publish: 橘子出版社>, <Publish: 蘋果出版社>]> >>>obj = models.Publish.objects.filter(pk=2).first() >>>obj # obj是一個model對象 <Publish: 橘子出版社> >>>model_to_dict(obj) {'id': 2, 'name': '橘子出版社', 'email': '456@qq.com'}
因而可知有幾個字段就轉化爲幾個鍵值對的字典。
serializers是django的序列化組件。
from django.views import View from .models import Publish import json class PublishView(View): def get(self, request): # QuerySet對象不能進行json序列化 # 方式3: from django.core import serializers publish_list = Publish.objects.all() ret = serializers.serialize("json", publish_list) return HttpResponse(json.dumps(ret)) def post(self, request): pass
注意:
def serialize(format, queryset, **options): """ Serialize a queryset (or any iterator that returns database objects) using a certain serializer. """ s = get_serializer(format)() s.serialize(queryset, **options) return s.getvalue()
傳遞給 serialize 方法的參數有二:一個序列化目標格式,另一個是序列化的對象QuerySet. (事實上,第二個參數能夠是任何可迭代的Django Model實例,但它不少狀況下就是一個QuerySet).
"[{\"model\": \"app01.publish\", \"pk\": 2, \"fields\": {\"name\": \"\\u6a58\\u5b50\\u51fa\\u7248\\u793e\", \"email\": \"456@qq.com\"}}, {\"model\": \"app01.publish\", \"pk\": 3, \"fields\": {\"name\": \"\\u82f9\\u679c\\u51fa\\u7248\\u793e\", \"email\": \"123@qq.com\"}}]"
from django.views import View from .models import Publish import json from rest_framework import serializers class PublishSerializers(serializers.Serializer): """爲QuerySet作序列化""" name = serializers.CharField() email = serializers.CharField() class PublishView(View): def get(self, request): # 方式4: publish_list = Publish.objects.all() ret = PublishSerializers(publish_list, many=True) # 描述是model對象仍是QuerySet True:queryset return HttpResponse(json.dumps(ret)) def post(self, request): pass
注意:
>>>from app01.views import PublishSerializers >>>publish_list = models.Publish.objects.all() >>>PublishSerializers(publish_list, many=True) # 描述是model對象仍是QuerySet PublishSerializers(<QuerySet [<Publish: 橘子出版社>, <Publish: 蘋果出版社>]>, many=True): name = CharField() email = CharField() >>>ps = PublishSerializers(publish_list, many=True) >>>ps.data [OrderedDict([('name', '橘子出版社'), ('email', '456@qq.com')]), OrderedDict([('name', '蘋果出版社'), ('email', '123@qq.com')])]
在序列化時,第一個參數傳遞要序列化的對象,第二個參數many是向組件聲明究竟是model對象仍是QuerySet。
many=True:QuerySet many=False:model對象(默認)
from django.shortcuts import render, HttpResponse from django.views import View from .models import * import json from rest_framework import serializers from rest_framework.views import APIView from rest_framework.response import Response 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 BookView(APIView): def get(self, request): book_list = Book.objects.all() bs = BookSerializers(book_list, many=True) # 序列化結果 # return HttpResponse(bs.data) return Response(bs.data) def post(self): pass
注意:
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")
配置了source='publish.name'參數後,BookSerializers在序列化時,"publish"再也不是取str(obj.publish),而是取obj.publish.name。頁面顯示以下所示:
能夠看到source字段在一對多字段比較好用,多對多字段顯示爲QuerySet,顯示不夠美觀。
在settings.py引入應用rest_framework:
INSTALLED_APPS = [ 'django.contrib.admin', ...... 'app01.apps.App01Config', 'rest_framework', ]
顯示效果:
source字段在一對多字段比較好用,多對多字段顯示爲QuerySet,顯示不夠美觀。
class BookSerializers(serializers.Serializer): authors = serializers.SerializerMethodField() def get_authors(self, obj): temp = [] for author in obj.authors.all(): temp.append(author.name) return temp
BookSerializers在序列化時,"authors"再也不是取obj.authors或者obj.authors.all(),而是取get_authors(obj)的返回值。注意這個方法必須是「get_"拼接配置了SerializerMethodField的字段。顯示效果以下:
須要對django model 的實例進行序列化。ModelSerializer 類提供了一個捷徑讓你能夠根據 Model 來建立 Serializer。
class BookModelSerializers(serializers.ModelSerializer): class Meta: # 幫忙轉換沒有本身寫的字段 model = Book fields = "__all__" 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 class BookView(APIView): def get(self, request): book_list = Book.objects.all() bs = BookModelSerializers(book_list, many=True) # 序列化結果 return Response(bs.data) def post(self): pass
注意:
class BookModelSerializers(serializers.ModelSerializer): class Meta: # 幫忙轉換沒有本身寫的字段 model = Book fields = "__all__"
顯示效果:
class BookModelSerializers(serializers.ModelSerializer): class Meta: # 幫忙轉換沒有本身寫的字段 model = Book fields = "__all__" 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
顯示效果以下所示:
BookModelSerializers也能夠將json數據轉爲QuerySet.
class BookView(APIView): def get(self, request):... 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 BookModelSerializers(serializers.ModelSerializer): class Meta: # 幫忙轉換沒有本身寫的字段 model = Book fields = "__all__"
在Postman提交json POST請求:
注意多對多字段必定要用列表組織數據。
提交POST請求後,當前添加數據顯示以下:
前面提交POST請求時,將BookModelSerializers去除了自定義配置。這由於ModelSerializer的create方法不支持source的用法。所以必須還自定義一個create方法。
class BookModelSerializers(serializers.ModelSerializer): class Meta: # 幫忙轉換沒有本身寫的字段 model = Book fields = "__all__" publish = serializers.CharField(source="publish.pk") def create(self, validated_data): print(validated_data) # {'publish': {'name': '1'}, 'title': 'go', 'price': 123, 'pub_date': datetime.date(2012, 12, 12)} authors = validated_data['authors'] # 添加記錄 book_obj = Book.objects.create(title=validated_data["title"], price=validated_data["price"], pub_date=validated_data["pub_date"], publish_id=validated_data["publish"]["pk"]) book_obj.authors.add(*authors) # 添加多對多的方式 return book_obj class BookView(APIView): def get(self, request): book_list = Book.objects.all() bs = BookModelSerializers(book_list, many=True) # 序列化結果 # return HttpResponse(bs.data) 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)
提交POST請求顯示效果以下:
class BookDetailView(APIView): def get(self, request, id): book_obj = Book.objects.filter(pk=id).first() print(book_obj) bs = BookModelSerializers(book_obj) return Response(bs.data) # 查看的單條數據 def put(self, request, id): book_obj = Book.objects.filter(pk=id).first() bs = BookModelSerializers(book_obj, data=request.data) # 作更新操做 if bs.is_valid(): # 校驗更新數據是否有問題 bs.save() # ModelSerializer類的update方法 return Response(bs.data) # 查看更新的數據 else: return HttpResponse(bs.errors) def delete(self, reqeust, book_id): Book.objects.filter(pk=book_id).delete() return Response() # 刪除操做返回空
注意:
from django.contrib import admin from django.urls import path, re_path from app01 import views urlpatterns = [ path('admin/', admin.site.urls), path('publishes/', views.PublishView.as_view()), re_path(r'^books/$', views.BookView.as_view()), re_path(r'^books/(\d+)/$', views.BookDetailView.as_view()) ]
from rest_framework import serializers from .models import * class BookModelSerializers(serializers.ModelSerializer): class Meta: # 幫忙轉換沒有本身寫的字段 model = Book fields = "__all__" # publish = serializers.CharField(source="publish.pk") # authors = serializers.SerializerMethodField() # def get_authors(self, obj): # temp = [] # for author in obj.authors.all(): # temp.append(author.name) # return temp # def create(self, validated_data): # print(validated_data) # {'publish': {'name': '1'}, 'title': 'go', 'price': 123, 'pub_date': datetime.date(2012, 12, 12)} # authors = validated_data['authors'] # # 添加記錄 # book_obj = Book.objects.create(title=validated_data["title"], price=validated_data["price"], # pub_date=validated_data["pub_date"], publish_id=validated_data["publish"]["pk"]) # book_obj.authors.add(*authors) # 添加多對多的方式 # return book_obj
再次發送get請求能夠發現id=4的這條數據已經刪除了。
class PublishModelSerializers(serializers.ModelSerializer): class Meta: model = Publish fields = "__all__" publish = serializers.HyperlinkedIdentityField( view_name='detail_publish', # detail_publish:url別名 lookup_field="publish_id", # publish_id:url中(\d+)的值 lookup_url_kwarg="pk") # pk:命名分組名稱
from django.contrib import admin from django.urls import path, re_path from app01 import views urlpatterns = [ path('admin/', admin.site.urls), re_path(r'^publishes/$', views.PublishView.as_view(), name="publish"), re_path(r'^publishes/(\d+)/$', views.PublishDetailView.as_view(), name="detail_publish"), re_path(r'^books/$', views.BookView.as_view(), name="books"), re_path(r'^books/(\d+)/$', views.BookDetailView.as_view(), name="detail_book") ]
命名分組就是給具備默認分組編號的組另外再給一個別名。命名分組的語法格式以下:
(?P<name>正則表達式) #name是一個合法的標識符
在這裏給(\d+)作命名分組:
from django.contrib import admin from django.urls import path, re_path from app01 import views urlpatterns = [ path('admin/', admin.site.urls), re_path(r'^publishes/$', views.PublishView.as_view(), name="publish"), re_path(r'^publishes/(?P<pk>\d+)/$', views.PublishDetailView.as_view(), name="detail_publish"), re_path(r'^books/$', views.BookView.as_view(), name="books"), re_path(r'^books/(?P<pk>\d+)/$', views.BookDetailView.as_view(), name="detail_book") ]
在使用了HyperlinkedIdentityField後,要求BookModelSerializers序列化時必須添加context={"request": request}
class BookView(APIView): def get(self, request): book_list = Book.objects.all() bs = BookModelSerializers(book_list, many=True, context={"request": request}) # 序列化結果 # return HttpResponse(bs.data) 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, pk): book_obj = Book.objects.filter(pk=pk).first() print(book_obj) bs = BookModelSerializers(book_obj, context={"request": request}) return Response(bs.data) # 查看的單條數據 def put(self, request, pk): book_obj = Book.objects.filter(pk=pk).first() bs = BookModelSerializers(book_obj, data=request.data) if bs.is_valid(): bs.save() return Response(bs.data) else: return HttpResponse(bs.errors) def delete(self, reqeust, book_id): Book.objects.filter(pk=book_id).delete() return Response() # 刪除操做返回空
接收前端傳過來的json處理是由Parser解析器執行,反序列化只進行驗證和保存。
當前端給DRF發post的請求的時候,前端給咱們傳過來的數據,要進行一些校驗再保存到數據庫。
這些校驗以及保存工做,DRF的Serializer也給咱們提供了一些方法了。首先要寫反序列化用的一些字段,這些字段要跟序列化區分開。Serializer提供了.is_valid() 和.save()方法。
SerDemo/serializers.py文件:
class BookSerializer(serializers.Serializer): """Book序列化類,注意與models對應""" id = serializers.IntegerField(required=False) # required=False設置該字段無需校驗 title = serializers.CharField(max_length=32) # ChoiceField字段處理 CHOICES = ((1, "Python"), (2, "Go"), (3, "Linux")) # choice字段配置source參數,顯示對應名,read_only設置只讀,只在序列化時使用 category = serializers.ChoiceField(choices=CHOICES, source='get_category_display', read_only=True) # 圖書的類別 # write_only設置只寫,只反序列化時使用 w_category = serializers.ChoiceField(choices=CHOICES, write_only=True) pub_time = serializers.DateField( ) # 當序列化與反序列化的類型不一樣時,須要分別生成read_only和write_only兩個字段 # 外鍵字段處理 publisher = PublisherSerializer(read_only=True) publisher_id = serializers.IntegerField(write_only=True) # 多對多字段處理(經過many字段與ForeignKey區分) author = AuthorSerializer(many=True, read_only=True) author_list = serializers.ListField(write_only=True) def create(self, validated_data): # 重寫save中的create方法 book_obj = 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_obj.author.add(*validated_data['author_list']) # 添加多對多 return book_obj
SerDemo/views.py文件:
# 方式三:基於rest_framework框架實現序列化(pip install djangorestframework) from rest_framework.views import APIView 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) # 使用序列化器序列化 """ 序列化的數據保存在ret.data中 """ return Response(ret.data) """ 得出來的結果會使用Django REST framework模板,在serializers.py中定製好序列化類後,顯示效果以下所示: HTTP 200 OK Allow: GET, HEAD, OPTIONS Content-Type: application/json Vary: Accept [ { "id": 1, "title": "python開發", "category": "Python", "pub_time": "2011-08-27", "publisher": { "id": 1, "title": "人民日報社" }, "author": [ { "id": 1, "name": "阿薩德" }, { "id": 2, "name": "阿加莎" } ] }, { "id": 2, "title": "go開發", "category": "Go", "pub_time": "2015-09-30", "publisher": { "id": 2, "title": "湖北日報社" }, "author": [ { "id": 2, "name": "於華吉" } ] }, { "id": 3, "title": "Linux開發", "category": "Linux", "pub_time": "2008-08-27", "publisher": { "id": 3, "title": "長江日報設" }, "author": [ { "id": 1, "name": "阿薩德" }, { "id": 3, "name": "阿迪力" } ] } ] """ def post(self, request): print(request.data) serializer = BookSerializer(data=request.data) # 序列化器校驗前端傳回來的數據 if serializer.is_valid(): serializer.save() # 驗證成功後保存數據庫 # 由於ModelSerializer的create方法不支持source的用法。所以必須還自定義一個create方法。 return Response(serializer.validated_data) # validated_data存放驗證經過的數據 else: return Response(serializer.errors) # errors存放錯誤信息 ''' 發送post請求接口設計 POST /books/list { "title": "nodejs的使用教程", "w_category": "1", "pub_time": "2018-10-27", "publisher_id": 1, "author_list": [1,2,3] } '''
SerDemo/serializers.py文件:
class BookSerializer(serializers.Serializer): """Book序列化類,注意與models對應""" id = serializers.IntegerField(required=False) # required=False設置該字段無需校驗 title = serializers.CharField(max_length=32) # ChoiceField字段處理 CHOICES = ((1, "Python"), (2, "Go"), (3, "Linux")) # choice字段配置source參數,顯示對應名,read_only設置只讀,只在序列化時使用 category = serializers.ChoiceField(choices=CHOICES, source='get_category_display', read_only=True) # 圖書的類別 # write_only設置只寫,只反序列化時使用 w_category = serializers.ChoiceField(choices=CHOICES, write_only=True) pub_time = serializers.DateField( ) # 當序列化與反序列化的類型不一樣時,須要分別生成read_only和write_only兩個字段 # 外鍵字段處理 publisher = PublisherSerializer(read_only=True) publisher_id = serializers.IntegerField(write_only=True) # 多對多字段處理(經過many字段與ForeignKey區分) author = AuthorSerializer(many=True, read_only=True) author_list = serializers.ListField(write_only=True) def create(self, validated_data): # 重寫save中的create方法 book_obj = 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_obj.author.add(*validated_data['author_list']) # 添加多對多 return book_obj def update(self, instance, validated_data): # 判斷對應項是否更新,若是更新則替換 instance.title = validated_data.get('title', instance.title) instance.category = validated_data.get('category', instance.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
SerDemo/views.py文件:
class BookEditView(APIView): def get(self, request, id): """ 查看單條數據 :param request: :param id: :return: """ book_obj = Book.objects.filter(id=id).first() ret = BookSerializer(book_obj) return Response(ret.data) ''' GET /books/retrieve/3 { "id": 3, "title": "Linux開發", "category": "Linux", "pub_time": "2008-08-27", "publisher": { "id": 3, "title": "長江日報社" }, "author": [ { "id": 1, "name": "阿薩德" }, { "id": 3, "name": "阿斯達" } ] } ''' 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.validated_data) # 返回驗證經過的數據 else: return Response(serializer.errors) # 返回驗證錯誤的數據
若是須要對一些字段進行自定義的驗證,DRF也提供了鉤子方法。
class BookSerializer(serializers.Serializer): """Book序列化類,注意與models對應""" id = serializers.IntegerField(required=False) # required=False設置該字段無需校驗 title = serializers.CharField(max_length=32) # 代碼省略 def validated_title(self, value): # 對字段進行驗證:校驗title字段 if "python" not in value.lower(): # 若是python不在value字段中 raise serializers.ValidationError("標題必須含有python") # 自定義錯誤信息 return value
在提交put請求時,若是提交{"title": 「go語言開發」},沒有包含python則會返回錯誤提示。
class BookSerializer(serializers.Serializer): """Book序列化類,注意與models對應""" id = serializers.IntegerField(required=False) # required=False設置該字段無需校驗 title = serializers.CharField(max_length=32) # 代碼省略 def validate(self, attrs): # 對多個字段進行比較驗證 # 執行更新操做:{"w_category": 1,"publisher_id": 1} # 注意JSON中,標準語法中,不支持單引號,屬性或者屬性值,都必須是雙引號括起來 if attrs['w_category'] == 1 and attrs['publisher_id'] == 1: # 聯合校驗分類和標題 return attrs else: raise serializers.ValidationError('分類以及出版社不符合要求') # 拋出異常
效果以下所示:
def my_validate(value): # 自定義驗證器 if "fuck" in value.lower(): raise serializers.ValidationError("不能含有敏感信息") else: return value class BookSerializer(serializers.Serializer): """Book序列化類,注意與models對應""" id = serializers.IntegerField(required=False) # required=False設置該字段無需校驗 title = serializers.CharField(max_length=32, validators=[my_validate]) # 添加自定義驗證器 # 代碼省略
此時title字段不只有了自定義的驗證器,又有了單個字段驗證,若是執行一個不知足兩個條件的更新請求:{"title":"fuck"}
返回結果以下所示: