RESTful規範(一)
一、學習restframework之前準備
1、json格式若想展示中文,需要ensure_ascii=False
import json dic={'name':'你好'} print(json.dumps(dic,ensure_ascii=False))
2、不基於restframework也可以通過django來做符合restframework的規範介面設計,Jsonresponse,若想在json展示中文,如下
def books(request): ll=[{'name':'python全站開發','price':20},{'name':'linux','price':30}] # return HttpResponse(json.dumps(ll)) return JsonResponse(ll,safe=False,json_dumps_params={'ensure_ascii':False})
3、原聲cbv執行流程
url(r'^books/$', views.Book.as_view()),
原聲cbv執行流程---》as_view----》dispatch---》相應到具體的函式 from django.views import View class Book(View): def get(self,reuquest): # reuquest.method return HttpResponse('get') def post(self,request): return HttpResponse('post')
3、分析restframework
一 restfu(規範) 是什麼: -面向資源程式設計 -getBooklist:獲取圖書列表 -符合規範的:books 規範: -method:get----》books----》取到所有的書 post———》books---》新增圖書 put/patch--》books/id---》修改圖書 delete---》books/id---》刪除圖書 -https://api.example.com/v1/zoos?limit=10 - 二 drf 安裝(app):pip3 install djangorestframework -基於drf寫resful的介面,得寫CBV -request物件,原始碼分析 -APIView原始碼分析
二、restframework使用
1、POST沒法解析json格式,可以在body裡取值出來序列化反序列化操作轉成字典,目前drf可以在前臺提交post後臺取值的話用data,
from rest_framework.views import APIView class Book(APIView): def get(self,request): # 拿原來的request物件 # request._request # print(request.method) # print(request._request.method) # request.POST # request.method return HttpResponse('get') def post(self,request): print(request.method) print(request._request.method) print(request.POST) # 用apiview之後,再取資料,從request.data print(request.data) return HttpResponse('post')
2、用Postman模擬發http請求,網上直接下載,data可以類似於解析器,解析form-data,urlencoded,json格式,而django只能解析form-data,urlencoded兩種格式
三、序列化元件
1、從資料庫取出來的都是qs型別,裡面套了一個一個物件,要傳到前臺去必須要json格式,符合drf規範的第一種方式
資料庫配置
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'resful', 'USER':'root', 'PASSWORD':'', 'HOST':'127.0.0.1', 'PORT':3306, } }
models取值為qureryset型別,
from app01 import models 序列化組建 第一種方式 class Book(APIView): def get(self,request): response={'status':100,'msg':None} books=models.Book.objects.all() # ll=[] # for book in books: # ll.append({'name':book.name,''}) ll=[ {'name':book.name,'price':book.price} for book in books] response['msg']='查詢成功' response['data']=ll return JsonResponse(response,safe=False) # return HttpResponse('get') def post(self,request): return HttpResponse('post')
2、第二種方式,用django子自帶序列化元件serializers(Django內建的serializers(把物件序列化成json字串),但是有個缺點不能定製化
from django.core import serializers class Book(APIView): def get(self,request): # response={'status':100,'msg':None} books = models.Book.objects.all() ret = serializers.serialize("json", books) return HttpResponse(ret) # return HttpResponse('get') def post(self,request): return HttpResponse('post')
3、用drf自帶的元件,建議的話新建個py在APP下,方便解耦,如myserial.py,為符合drf格式,裡面新建個response類,見下
-1 匯入:from rest_framework import serializers -2 寫一個類(名字任意),繼承serializers.Serializer class BookSer(serializers.Serializer): nid=serializers.IntegerField() name3=serializers.CharField(source='name') price=serializers.CharField() # publish_date = serializers.DateField() publish_date = serializers.CharField() # publish=serializers.CharField(source='publish.email') publish=serializers.CharField(source='publish.name') xxx=serializers.CharField(source='test') -3 如果不指定source,欄位名,必須跟資料庫列名一致 -4 source--》既可以指定資料屬性,又可以指定方法屬性,可以寫(publish.name) -5 使用: -查詢出要序列化的資料:books = models.Book.objects.all() -ret=myserial.BookSer(books,many=True)-----》多條(queryset物件),必須指定many=True -ret=myserial.BookSer(books,many=False)-----》一條(Book物件),必須指定many=False -6 aa=serializers.SerializerMethodField() -必須配套一個方法(get_aa(self,obj)),方法返回結果,會賦給aa -在方法內部,可以繼續用序列化元件
myserial.py
from rest_framework import serializers class BookSer(serializers.Serializer): nid=serializers.IntegerField() name3=serializers.CharField(source='name') price=serializers.CharField() # publish_date = serializers.DateField() publish_date = serializers.CharField() # publish=serializers.CharField(source='publish.email') publish=serializers.CharField(source='publish.name') xxx=serializers.CharField(source='test') # authors=serializers.CharField(source='authors.all') # SerializerMethodField,可以寫一個方法方法名叫:get_欄位名字,方法返回值,會賦給authors aa=serializers.SerializerMethodField() # def get_authors(self,obj): # authors=obj.authors.all() # # ll=[ author.name for author in authors] # ll=[ {'name':author.name,'age':author.age} for author in authors] # return ll def get_aa(self, obj): authors = obj.authors.all() # ll=[ author.name for author in authors] ser=AuthorSer(authors,many=True) return ser.data
models.py
class Book(models.Model): nid = models.AutoField(primary_key=True) name = models.CharField(max_length=32) price = models.DecimalField(max_digits=5, decimal_places=2) publish_date = models.DateField(auto_now_add=True) publish = models.ForeignKey(to='Publish',to_field='nid',on_delete=models.CASCADE) authors=models.ManyToManyField(to='Author') def test(self): return 'ttttttt' def __str__(self): return self.name
使用,為符合drf格式,裡面新建個response類,@property讓類方法變屬性,不需要(),直接點+屬性就ok了
class MyResponse(): def __init__(self): self.status = 100 self.msg = None @property def get_dic(self): return self.__dict__from app01 import myserial
class Book(APIView): queryset=models.Book.objects.all() serializer_class=myserial.BookSer def get(self,request): response=MyResponse() # 多條 # books = models.Book.objects.all() # ret=myserial.BookSer(books,many=True) # 一條 book = self.queryset # ret = myserial.BookSer(book, many=False) ret = self.serializer_class(instance=book, many=True) response.msg='查詢成功' response.data=ret.data return JsonResponse(response.get_dic,safe=False) # return HttpResponse('get') def post(self,request):
# return HttpResponse('post')
4、若要進行多對多的查詢,book和author,可以用SerializerMethodField方法
-6 aa=serializers.SerializerMethodField() -必須配套一個方法(get_aa(self,obj)),方法返回結果,會賦給aa -在方法內部,可以繼續用序列化元件
如下
class BookSer(serializers.Serializer): nid=serializers.IntegerField() name3=serializers.CharField(source='name') price=serializers.CharField() # publish_date = serializers.DateField() publish_date = serializers.CharField() # publish=serializers.CharField(source='publish.email') publish=serializers.CharField(source='publish.name') xxx=serializers.CharField(source='test') # authors=serializers.CharField(source='authors.all') # SerializerMethodField,可以寫一個方法方法名叫:get_欄位名字,方法返回值,會賦給authors aa=serializers.SerializerMethodField() # def get_authors(self,obj): # authors=obj.authors.all() # # ll=[ author.name for author in authors] # ll=[ {'name':author.name,'age':author.age} for author in authors] # return ll def get_aa(self, obj): authors = obj.authors.all() # ll=[ author.name for author in authors] ser=AuthorSer(authors,many=True) return ser.data class AuthorSer(serializers.Serializer): id=serializers.IntegerField(source='nid') age=serializers.CharField() name=serializers.CharField()
四 序列化元件之serializers.ModelSerializer
-用法同Serializer -不同點: class BookSer(serializers.ModelSerializer): class Meta: # 指定要序列號的表模型是book model=models.Book fields='__all__' exclude=['nid'] depth=1
例子如下
from app01 import models class BookSer(serializers.ModelSerializer): class Meta: # 指定要序列號的表模型是book model=models.Book # 把所有欄位都序列化 # fields='__all__' # 可以傳列表,指定取幾個 # fields=['name','authors','publish'] # 除了nid都查 exclude=['authors'] #fields和exclude不能同時用 # depth指定深度,個人建議最多用3 # depth=2
五 序列化元件的欄位校驗和反序列化功能
序列化元件是將物件序列化成字典,但是前臺post提交的json資料需要轉成字典,再反序列化成物件,最後save()方法才能儲存在資料裡,需要反序列功能
-只有:ModelSerializer,能直接儲存 - def post(self,request): print(request.data) #生成一個序列化物件 ser=myserial.BookSer(data=request.data) #判斷欄位是否校驗通過 if ser.is_valid(): #通過,直接儲存 ser.save() else: #錯誤資訊 print(ser.errors) return HttpResponse('post')
from app01 import models
class BookSer(serializers.ModelSerializer):
class Meta:
# 指定要序列號的表模型是book
model=models.Book
exclude=['authors']
如下
def post(self,request): # print(request.data) ser=myserial.BookSer(data=request.data) if ser.is_valid(): ser.save() return HttpResponse('成功') else: print(ser.errors) return JsonResponse(ser.errors)
六 序列化元件區域性校驗和全域性校驗
-區域性校驗 name=serializers.CharField() def validate_name(self,value): if value.startswith('sb'): raise ValidationError('不能以sb開頭') else: return value -全域性校驗 def validate(self,value): print(type(value)) print(value) name=value.get('name') price=value.get('price') if name!=price: raise ValidationError('書名和價格不相等') else: return value
如下
from rest_framework.exceptions import ValidationError from app01 import models class BookSer(serializers.ModelSerializer): class Meta: # 指定要序列號的表模型是book model=models.Book # 把所有欄位都序列化 # fields='__all__' # 可以傳列表,指定取幾個 # fields=['name','authors','publish'] # 除了nid都查 exclude=['authors'] #fields和exclude不能同時用 # depth指定深度,個人建議最多用3 # depth=2 區域性校驗 name=serializers.CharField(error_messages={'required':'該欄位必填'}) def validate_name(self,value): if value.startswith('sb'): raise ValidationError('不能以sb開頭') else: return value 全域性校驗 def validate(self,value): print(type(value)) print(value) name=value.get('name') price=value.get('price') if name!=price: raise ValidationError('書名和價格不相等') else: return value
views,主要驗證前臺post提交資料的區域性和全域性驗證
from app01 import myserial class Book(APIView): queryset=models.Book.objects.all() serializer_class=myserial.BookSer def get(self,request): response=MyResponse() # 多條 # books = models.Book.objects.all() # ret=myserial.BookSer(books,many=True) # 一條 book = self.queryset # ret = myserial.BookSer(book, many=False) ret = self.serializer_class(instance=book, many=True) response.msg='查詢成功' response.data=ret.data return JsonResponse(response.get_dic,safe=False) # return HttpResponse('get') def post(self,request): # print(request.data) ser=myserial.BookSer(data=request.data) if ser.is_valid(): ser.save() return HttpResponse('成功') else: print(ser.errors) return JsonResponse(ser.errors)
七、符合drf的檢視類基本寫法
1、views.py
class MyResponse():
def __init__(self):
self.status = 100
self.msg = None
@property
def get_dic(self):
return self.__dict__
class BookDetail(APIView): def get(self,request,id): response=MyResponse() ret=models.Book.objects.filter(pk=id).first() ser=myserial.BookSer(instance=ret,many=False) response.msg='查詢成功' response.data=ser.data return JsonResponse(response.get_dic,safe=False) def put(self,request,id): # 修改 response=MyResponse() book=models.Book.objects.filter(pk=id).first() ser=myserial.BookSer(instance=book,data=request.data) if ser.is_valid(): # 可以新增,可以修改 ser.save() print(ser.data) print(type(ser.instance)) response.msg='修改成功' response.data=ser.data else: response.msg = '修改失敗' response.status = 101 response.data=ser.errors return JsonResponse(response.get_dic,safe=False) def delete(self,request,id): ret=models.Book.objects.filter(pk=id).delete() return HttpResponse('刪除成功')
2、序列化元件
url(r'^books/(?P<pk>\d+)/', views.BookDetail.as_view()),
from app01 import models
class BookSer(serializers.ModelSerializer):
class Meta:
# 指定要序列號的表模型是book
model=models.Book
exclude=['authors']