1.RESTful
1.1 定義
REST(Representational State Transfer)與技術無關,代表一種軟件架構風格,中文為表征狀態轉移。
1.2 RESTful API設計
-
API與用戶的通信協議,總是使用HTTPS協議
-
域名
- https://api.example.com 盡量將API部署在專用域名(會存在跨域問題)
- https://example.org/api/ API在路徑上,簡單
-
版本
- URL 如:https://api.example.com/v1/ 代表v1版本的API
-
路徑,網絡上的任何東西都是資源,均使用名詞表示(可復數)
-
method
- GET :從服務器取出資源(一項或多項)
- POST:在服務器新建一個資源
- PUT:在服務器更新資源(客戶端提供改變后的完整資源)
- PATCH:在服務器更新資源(客戶端提供改變屬性)
- DELETE:從服務器刪除資源
-
過濾,通過在url上傳遞參數的形式傳遞過濾條件
-
狀態碼
常見狀態碼:
200 OK - [GET]:服務器成功返回用戶請求的數據,該操作是冪等的(Idempotent)。 201 CREATED - [POST/PUT/PATCH]:用戶新建或修改數據成功。 202 Accepted - [*]:表示一個請求已經進入后台排隊(異步任務) 204 NO CONTENT - [DELETE]:用戶刪除數據成功。 400 INVALID REQUEST - [POST/PUT/PATCH]:用戶發出的請求有錯誤,服務器沒有進行新建或修改數據的操作,該操作是冪等的。 401 Unauthorized - [*]:表示用戶沒有權限(令牌、用戶名、密碼錯誤)。 403 Forbidden - [*] 表示用戶得到授權(與401錯誤相對),但是訪問是被禁止的。 404 NOT FOUND - [*]:用戶發出的請求針對的是不存在的記錄,服務器沒有進行操作,該操作是冪等的。 406 Not Acceptable - [GET]:用戶請求的格式不可得(比如用戶請求JSON格式,但是只有XML格式)。 410 Gone -[GET]:用戶請求的資源被永久刪除,且不會再得到的。 422 Unprocesable entity - [POST/PUT/PATCH] 當創建一個對象時,發生一個驗證錯誤。 500 INTERNAL SERVER ERROR - [*]:服務器發生錯誤,用戶將無法判斷發出的請求是否成功。 -
錯誤信息,狀態碼是4xx時,應該返回錯誤信息,error當做key
{ error:"Invalid API key" } -
返回結果,針對不同操作,服務器向用戶返回的結果應該符合以下規范
GET /collection:返回資源對象的列表(數組) GET /collection/resource:返回單個資源對象 POST /collection:返回新生成的資源對象 PUT /collection/resource:返回完整的資源對象 PATCH /collection/resource:返回完整的資源對象 DELETE /collection/resource:返回一個空文檔 -
返回url
例如訪問一個接口來獲取員工信息,而我們需要的數據中由員工所屬部門信息。
需要跨表查詢,這時restful建議部門信息返回一個url,然后用戶再向該接口發送請求拿到部門信息
## 2.Django Rest Framework 框架
Django Rest Framework幫助我們快速構建符合RESTful的數據。通常用於前后端分離項目之間的數據資源傳輸。
### 2.1 Serializer的簡單使用
serializers序列化器,幫助我們快速序列化在數據庫中查詢到的數據。
下面通過一個小例子來簡單看一下Serializer的使用
表結構model.py
```python
from django.db import models
# Create your models here.
class Publisher(models.Model):
name = models.CharField(max_length=32)
class Author(models.Model):
name = models.CharField(max_length=32, )
class Book(models.Model):
title = models.CharField(max_length=32)
price = models.DecimalField(max_digits=6, decimal_places=2)
pub_date = models.DateTimeField()
pub = models.ForeignKey('Publisher', on_delete=models.CASCADE)
authors = models.ManyToManyField('Author')
路由urls.py
from django.conf.urls import url
from app01 import views
urlpatterns = [
url(r'^books/', views.BookListView.as_view()),
]
2.1.1 通過json模塊手動序列化
views.py文件
from django.shortcuts import render, HttpResponse
from django.views import View
import json
#使用json模塊序列化,時間類型需要單獨轉成字符串再序列化
class BookListView(View):
def get(self, request, *args, **kwargs):
obj_list = models.Book.objects.all().values('title', 'pub', 'pub_date')
return HttpResponse(json.dumps(list(obj_list), ensure_ascii=False)) # pub_date為日期類型無法json序列化
通過json手動序列化,當數據中有時間類型時,序要單獨處理成字符串類型,才能進行序列化。
2.1.2 通過django的JsonResponse
views.py文件
from django.http.response import JsonResponse
#使用JsonResponse對象序列化,可以直接處理時間類型
class BookListView(View):
def get(self, request, *args, **kwargs):
obj_list = models.Book.objects.all().values('title', 'pub', 'pub_date')
return JsonResponse(list(obj_list), safe=False, json_dumps_params={
'ensure_ascii': False}) # safe參數為False:當最外層不為字典時也可序列化,json_dumps_params參數:設置json序列化的參數
JsonResponse類不能對查詢到的query_set對象直接序列化,對外鍵等字段關聯對象數據不好處理。
2.1.3 使用django自帶的序列化器
views.py文件
#使用django自帶的序列化器
from django.core import serializers
class BookListView(View):
def get(self, request, *args, **kwargs):
""" 獲取所有的數據 返回所有的書籍數據 json格式"""
# 1. 從數據庫獲取所有數據
all_books = models.Book.objects.all()
data = serializers.serialize('json', all_books)
return HttpResponse(data)
序列化后的數據格式
[
{
"model": "app01.book",
"pk": 1,
"fields": {
"title": "\u4e5d\u9634\u771f\u7ecf",
"price": "9.99",
"pub_date": "2019-06-04T07:15:56Z",
"pub": 1,
"authors": [
1,2
]
}
},
{
"model": "app01.book",
"pk": 2,
"fields": {
"title": "\u4e5d\u9633\u771f\u7ecf",
"price": "98.30",
"pub_date": "2019-04-16T07:16:39Z",
"pub": 1,
"authors": [
1
]
}
},
{
"model": "app01.book",
"pk": 3,
"fields": {
"title": "\u8475\u82b1\u5b9d\u5178",
"price": "65.50",
"pub_date": "2019-07-25T07:17:09Z",
"pub": 2,
"authors": [
2
]
}
}
]
2.1.4 使用Rest Framework的Response對象
views.py文件
#使用RestFramework 的response對象
from rest_framework.views import APIView
from rest_framework.response import Response
class BookListView(APIView):
def get(self, request, *args, **kwargs):
all_books = models.Book.objects.all().values('title', 'authors__name','pub_date')
print(all_books)
return Response(all_books)
數據格式
[
{
"title": "九陰真經",
"authors__name": "沙和尚",
"pub_date": "2019-06-04T07:15:56Z"
},
{
"title": "九陰真經",
"authors__name": "豬悟能",
"pub_date": "2019-06-04T07:15:56Z"
},
{
"title": "九陽真經",
"authors__name": "沙和尚",
"pub_date": "2019-04-16T07:16:39Z"
},
{
"title": "葵花寶典",
"authors__name": "豬悟能",
"pub_date": "2019-07-25T07:17:09Z"
}
]
2.1.5 使用Rest Framework的序列化器
首先要創建一個序列化器
serializer.py文件
from rest_framework import serializers
class AuthorSerializer(serializers.Serializer): #Author表的序列化器
name = serializers.CharField()
class PublisherSerializer(serializers.Serializer): #Publisher表的序列化器
name = serializers.CharField()
class BookSerializer(serializers.Serializer): #Book表的序列化器
title = serializers.CharField() #普通字段類型和數據庫類型相同即可,字段名也要相同
price = serializers.DecimalField(max_digits=6,decimal_places=2)
pub_date = serializers.DateTimeField()
pub = PublisherSerializer() #外鍵使用,使用定義的序列化器實例化
authors = serializers.SerializerMethodField() #多對多字段,定義get_字段名方法
def get_authors(self,obj): #obj當前表的對象
data_obj = AuthorSerializer(obj.authors.all(),many=True) #多條數據要設置many為True
return data_obj.data #返回對象的data
views.py文件
class BookListView(APIView):
def get(self, request, *args, **kwargs):
all_books = models.Book.objects.all()
ser_obj = BookSerializer(all_books, many=True)#多條數據要設置many為True
return Response(ser_obj.data)#返回對象的data
數據格式:
[
{
"title": "九陰真經",
"price": "9.99",
"pub_date": "2019-06-04T07:15:56Z",
"pub": {
"name": "沙和尚出版社"
},
"authors": [
{
"name": "沙和尚"
},
{
"name": "豬悟能"
}
]
},
{
"title": "九陽真經",
"price": "98.30",
"pub_date": "2019-04-16T07:16:39Z",
"pub": {
"name": "沙和尚出版社"
},
"authors": [
{
"name": "沙和尚"
}
]
},
{
"title": "葵花寶典",
"price": "65.50",
"pub_date": "2019-07-25T07:17:09Z",
"pub": {
"name": "二師兄出版社"
},
"authors": [
{
"name": "豬悟能"
}
]
}
]
2.2 使用Serializer實現簡單的增刪改
urls.py文件
from django.conf.urls import url
from app01 import views
urlpatterns = [
url(r'^books/', views.BookListView.as_view()), #獲取多條數據和添加數據
url(r'^book/(\d+)/', views.BookView.as_view()), #獲取單條數據,編輯和刪除數據
]
2.2.1 獲取多條數據和添加數據
serializer.py文件
from rest_framework import serializers
from app01 import models
class AuthorSerializer(serializers.Serializer):
id = serializers.IntegerField()
name = serializers.CharField()
class PublisherSerializer(serializers.Serializer):
name = serializers.CharField()
class BookSerializer(serializers.Serializer):
title = serializers.CharField()
price = serializers.DecimalField(max_digits=6, decimal_places=2)
pub_date = serializers.DateTimeField()
pub = PublisherSerializer(read_only=True) #GET請求時需要的數據設置為只讀,POST時可為空
authors = serializers.SerializerMethodField(read_only=True)
post_pub = serializers.IntegerField(write_only=True) #POST和PUT時的數據只寫,GET時可為空
post_authors = serializers.ListField(write_only=True)
def get_authors(self, obj):
data_obj = AuthorSerializer(obj.authors.all(), many=True) #多個對象需要many為True
return data_obj.data
def create(self, validated_data): #create方法用於添加數據,validated_data校驗完的數據
book_obj = models.Book.objects.create(
title=validated_data.get('title'),
price=validated_data.get('price'),
pub_date=validated_data.get('pub_date'),
pub_id=validated_data.get('post_pub'),
)
book_obj.authors.set(validated_data.get('post_authors'))
return book_obj # 將book對象返回
view.py文件
from app01.serializer import BookSerializer
class BookListView(APIView):
def get(self, request, *args, **kwargs):
all_books = models.Book.objects.all()
obj = BookSerializer(all_books,many=True)
return Response(obj.data)
def post(self,request,*args,**kwargs):
print(request.data)
obj = BookSerializer(data=request.data)
if obj.is_valid(): #對數據進行校驗,貌似序列化器里的多對多不會校驗
obj.save() #會去序列化器里找create()方法
return Response(obj.data) #校驗成功返回添加的數據
return Response({'error': obj.errors}) #校驗失敗返回錯誤信息
POST的數據
{
"title": "葵花寶典續集",
"price": "65.50",
"pub_date": "2019-07-25T07:17:09Z",
"post_pub": 1,
"post_authors": [
1
]
}
返回的數據
{
"title": "葵花寶典續集",
"price": "65.50",
"pub_date": "2019-07-25T07:17:09Z",
"pub": {
"name": "沙和尚出版社"
},
"authors": [
{
"id": 1,
"name": "沙和尚"
}
]
}
2.2.2 獲取單條數據、編輯和修改
serializer.py文件
class BookSerializer(serializers.Serializer):
title = serializers.CharField()
price = serializers.DecimalField(max_digits=6, decimal_places=2)
pub_date = serializers.DateTimeField()
pub = PublisherSerializer(read_only=True)
authors = serializers.SerializerMethodField(read_only=True)
post_pub = serializers.IntegerField(write_only=True)
post_authors = serializers.ListField(write_only=True)
def get_authors(self, obj):
data_obj = AuthorSerializer(obj.authors.all(), many=True)
return data_obj.data
def create(self, validated_data): # validated_data校驗完的數據
book_obj = models.Book.objects.create(
title=validated_data.get('title'),
price=validated_data.get('price'),
pub_date=validated_data.get('pub_date'),
pub_id=validated_data.get('post_pub'),
)
book_obj.authors.set(validated_data.get('post_authors'))
return book_obj # 將book對象返回
def update(self, instance, validated_data): # 修改數據時調用update方法,instance當前要求改的數據對象
instance.title = validated_data.get('title', instance.title)
instance.price = validated_data.get('price', instance.price)
instance.pub_date = validated_data.get('pub_date', instance.pub_date)
instance.pub_id = validated_data.get('post_pub', instance.pub_id)
instance.save()
instance.authors.set(validated_data.get('post_authors',instance.authors.all()))
return instance #返回修改后的數據對象
view.py文件
class BookView(APIView):
def get(self, request,pk, *args, **kwargs):
book = models.Book.objects.filter(pk=pk).first()
obj = BookSerializer(book)
return Response(obj.data)
def put(self,request,pk,*args,**kwargs): #修改資源
book = models.Book.objects.filter(pk=pk).first()
obj = BookSerializer(data=request.data,instance=book,partial=True) #partial為True表示可以只傳修改的那部分值
if obj.is_valid():
obj.save() #保存時會調用序列化器定義好的update()方法
return Response(obj.data) #校驗成功返回修改后的對象
return Response(obj.errors)
def delete(self,request,pk,*args,**kwargs): #刪除資源
book = models.Book.objects.filter(pk=pk).first()
if book:
book.delete()
return Response({'info':'刪除成功'})
return Response({'info': '數據不存在'})
PUT的數據
{
"title": "葵花寶典1",
"price": "63.50",
"pub_date": "2019-07-25T07:17:09Z",
"post_pub": 1,
"post_authors": [
1
]
}
#也可只穿修改的值
{
"title": "葵花寶典1" #注:只有一個元素,不要加逗號,不然校驗不通過
}
返回的數據
{
"title": "葵花寶典1",
"price": "63.50",
"pub_date": "2019-07-25T07:17:09Z",
"pub": {
"name": "沙和尚出版社"
},
"authors": [
{
"id": 1,
"name": "沙和尚"
}
]
}
2.3 Serializer的校驗
除了在定義序列化器根據定義的字段類型進行校驗外,我們還可以自定義校驗
2.3.1 自定義校驗器
serializer.py文件
def my_validator(value): #value為要校驗的值
if '大' in value:
raise serializers.ValidationError('標題包含敏感字符') #校驗不通過拋出異常
return value #校驗通過返回當前值
class BookSerializer(serializers.Serializer):
title = serializers.CharField(validators=[my_validator]) #自定義校驗器my_validator對title字段進行校驗
price = serializers.DecimalField(max_digits=6, decimal_places=2)
pub_date = serializers.DateTimeField()
pub = PublisherSerializer(read_only=True)
authors = serializers.SerializerMethodField(read_only=True)
post_pub = serializers.IntegerField(write_only=True)
post_authors = serializers.ListField(write_only=True)
發送的數據
{
"title": "大小",
"price": "99.30",
"pub_date": "2019-04-16T07:16:39Z",
"post_pub": 1,
"post_authors": [
1
]
}
返回的數據
{
"error": {
"title": [
"標題包含敏感字符"
]
}
}
2.3.2 局部鈎子函數
局部鈎子函數寫在序列化器類內部,可以對字段進行校驗。
serializer.py文件
class BookSerializer(serializers.Serializer):
title = serializers.CharField()
price = serializers.DecimalField(max_digits=6, decimal_places=2)
pub_date = serializers.DateTimeField()
pub = PublisherSerializer(read_only=True)
authors = serializers.SerializerMethodField(read_only=True)
post_pub = serializers.IntegerField(write_only=True)
post_authors = serializers.ListField(write_only=True)
def validate_title(self, value): #局部鈎子 validate_字段名
if '大' in value:
raise serializers.ValidationError('標題包含敏感字符')
return value
發送數據和接收數據同自定義校驗器。
2.3.3 全局鈎子函數
全局鈎子可以對所有字段進行校驗。
serializer.py文件
class BookSerializer(serializers.Serializer):
title = serializers.CharField()
price = serializers.DecimalField(max_digits=6, decimal_places=2)
pub_date = serializers.DateTimeField()
pub = PublisherSerializer(read_only=True)
authors = serializers.SerializerMethodField(read_only=True)
post_pub = serializers.IntegerField(write_only=True)
post_authors = serializers.ListField(write_only=True)
def validate(self, attrs):
if '大' in attrs['title']: #attr包含所有數據{'title': '大小', 'price': '99.30', 'pub_date': '2019-04-16T07:16:39Z', 'post_pub': 1, 'post_authors': [1]}
raise serializers.ValidationError('標題包含敏感字符')
return attrs #校驗通過返回所有數據
發送數據和接收數據同自定義校驗器。
2.4 ModelSerializer的簡單使用
ModelSerializer序列化器,使用起來比Serializer更加方便,它直接和Django的ORM的model表關聯了起來。對數據庫的數據的序列化更加方便。
serializer.py文件
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = models.Book #關聯的model
fields = '__all__' #所有的字段
GET到的數據
[
{
"id": 2,
"title": "九陽真經續集",
"price": "93.30",
"pub_date": "2019-04-16T07:16:39Z",
"pub": 1,
"authors": [
1
]
},
{
"id": 3,
"title": "葵花寶典",
"price": "65.50",
"pub_date": "2019-07-25T07:17:09Z",
"pub": 2,
"authors": [
2
]
}
]
這時外鍵和多對多等字段請求的只是id,下面一種簡單的解決方式
serializer.py文件
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = models.Book
fields = '__all__'
depth = 1 #深度為1
GET到的數據
[
{
"id": 2,
"title": "九陽真經續集",
"price": "93.30",
"pub_date": "2019-04-16T07:16:39Z",
"pub": {
"id": 1,
"name": "沙和尚出版社"
},
"authors": [
{
"id": 1,
"name": "沙和尚"
}
]
},
{
"id": 3,
"title": "葵花寶典",
"price": "65.50",
"pub_date": "2019-07-25T07:17:09Z",
"pub": {
"id": 2,
"name": "二師兄出版社"
},
"authors": [
{
"id": 2,
"name": "豬悟能"
}
]
}
]
但是這種方式在提交的時候變得不方便,所以一般通過下列方式來獲取外鍵的詳細數據
serializer.py文件
class BookSerializer(serializers.ModelSerializer):
pub_info = serializers.SerializerMethodField(read_only=True) #GET時需要該字段
authors_info = serializers.SerializerMethodField(read_only=True)
def get_pub_info(self,obj):
return PublisherSerializer(obj.pub).data
def get_authors_info(self,obj):
return AuthorSerializer(obj.authors.all(),many=True).data
class Meta:
model = models.Book
fields = '__all__'
extra_kwargs = { #額外的設置
'pub':{'write_only':True}, #設置字段的屬性,POST和PUT時需要該字段
'authors':{'write_only':True},
}
