RESTful API介紹,基於Django實現RESTful API,DRF 序列化

1.rest framework serializer(序列化)的簡單使用
	QuerySet([obj,obj,obj])	--> JSON格式數據
	
	0.安裝和導入:
		pip3 install djangorestframework
		
		from rest_framework import serializers
	
	1.簡單使用
		1.建立一個類,類必定要繼承serializers.Serializer
		
		2.chocie字段和FK字段均可以經過使用source來獲取對應的值
		
		3.多對多字段可使用serializers.SerializerMethodField
			def get_tag(self,obj):
				tag_list = []
				for i in obj.tag.all():
					tag_list.append(i.name)
				return tag_list
				
	2.使用ModelSerializer
		
		經過配置class Meta:
					model = 表名
					fields = ['字段',...]
					depth = 1
					
			

1、RESTful API介紹

什麼是RESTful 

REST與技術無關,表明的是一種軟件架構風格,REST是Representational State Transfer的簡稱,中文翻譯爲「表徵狀態轉移」或「表現層狀態轉化」。html

RESTful API設計

API與用戶的通訊協議

老是使用HTTPs協議。

如今互聯網企業,都開始關注安全了。因此,通常暴露的接口,都是使用https協議。前提是,須要購買SSL域名證書,一年花費,少則幾千,多則上萬。前端

域名 

https://api.example.com                         儘可能將API部署在專用域名

https://example.org/api/                        API很簡單

使用第一種,可能會有跨域問題。爲了不這種問題,能夠採用第二種。python

版本

1.  將版本信息放在URL中,如:https://api.example.com/v1/

2. 將版本信息放在請求頭中。

國內的公司,通常使用第一種。前端代碼不用大改動,後端開發好v2版本後。將url切換一下,就能夠實現平滑遷移。程序員

國外公司,使用會第二種,由於這樣比較安全!面試

路徑

視網絡上任何東西都是資源,均使用名詞表示(可複數)

https://api.example.com/v1/zoos

https://api.example.com/v1/animals

https://api.example.com/v1/employees

 上面的3個url分別表示:動物園、動物、員工sql

GET      :從服務器取出資源(一項或多項)

POST    :在服務器新建一個資源

PUT      :在服務器更新資源(客戶端提供改變後的完整資源)

PATCH  :在服務器更新資源(客戶端提供改變的屬性)

DELETE :從服務器刪除資源

PATCH不多用到數據庫

過濾

經過在url上傳參的形式傳遞搜索條件

https://api.example.com/v1/zoos?limit=10:指定返回記錄的數量

https://api.example.com/v1/zoos?offset=10:指定返回記錄的開始位置

https://api.example.com/v1/zoos?page=2&per_page=100:指定第幾頁,以及每頁的記錄數

https://api.example.com/v1/zoos?sortby=name&order=asc:指定返回結果按照哪一個屬性排序,以及排序順序

https://api.example.com/v1/zoos?animal_type_id=1:指定篩選條件

上面的url參數,一眼望過去,就大概知道啥意思了。因此命令規範,很重要!django

程序員寫代碼,不僅是完成功能而已!寫代碼的時候,必須按照規範來。好比python,遵循PEP8。json

已經寫好的代碼,有空的時候,將代碼優化一下!這樣,當你把項目交接給別人的時候,不至於,讓人看着代碼,晦澀難懂!一萬匹馬在心裏崩騰,有木有?後端

狀態碼

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 - [*]:服務器發生錯誤,用戶將沒法判斷髮出的請求是否成功。

  

幾個經常使用的http狀態碼,須要知道。由於面試必問!

錯誤處理

狀態碼是4xx時,應返回錯誤信息,error當作key。

{
    error: "Invalid API key"
}

儘可能將代碼寫的健全一點,遇到錯誤時,拋出error

返回結果

針對不一樣操做,服務器向用戶返回的結果應該符合如下規範

GET /collection:返回資源對象的列表(數組)
GET /collection/resource:返回單個資源對象
POST /collection:返回新生成的資源對象
PUT /collection/resource:返回完整的資源對象
PATCH /collection/resource:返回完整的資源對象
DELETE /collection/resource:返回一個空文檔

好比url:  http://127.0.0.1/api/comment/2 表示id爲2的詳細信息

Hypermedia API

RESTful API最好作到Hypermedia,即返回結果中提供連接,連向其餘API方法,使得用戶不查文檔,也知道下一步應該作什麼。

{"link": {
  "rel":   "collection https://www.example.com/zoos",
  "href":  "https://api.example.com/zoos",
  "title": "List of zoos",
  "type":  "application/vnd.yourformat+json"
}}

這個,要看需求了,有需求,能夠作一下!

更多內容,請參考:阮一峯的Blog

 

2、基於Django實現RESTful API

在昨天項目about_drf的基礎上, 增長一個評論表

修改model.py,增長表comment

# 評論表
class Comment(models.Model):
    content = models.CharField(max_length=128)
    article = models.ForeignKey(to='Article', on_delete=models.CASCADE)

使用2個命令,生成表

python manage.py makemigrations
python manage.py migrate

如今須要對評論表,作增刪改查,按照常規來說,會這麼寫url

url(r'comment_list/',...),
url(r'add_comment/',...),
url(r'delete_comment/',...),
url(r'edit_comment/',...),

如今還只是一個表的增刪改查,若是有80個表呢?寫320個url ?

RESTful API之url設計

路由分發

在app01(應用名)目錄下,建立文件app01_urls.py

 1 from django.conf.urls import url
 2 from app01 import views
 3 
 4 urlpatterns = [
 5     url(r'^article_list/', views.article_list),
 6     url(r'article_detail/(\d+)', views.article_detail),
 7 
 8     # 評論
 9     url(r'comment/', views.Comment.as_view()),
10 ]

include表示導入py文件

RESTful API之視圖

CBV

針對4種請求方式,使用FBV

def comment(request):
    if request.method == "GET":
        return HttpResponse("獲取評論")
    elif request.method == "POST":
        return HttpResponse("建立新評論")
    elif request.method == "PUT":
        return HttpResponse("修改評論")
    elif request.method == "DELETE":
        return HttpResponse("刪除評論")

這樣寫的話,一個4層if判斷,就會有很冗長的代碼。不利與維護,推薦使用CBV方式

CBV方式

from django import views
class Comment(views.View):
    def get(self, request):
        return HttpResponse("獲取評論")

    def post(self, request):
        return HttpResponse("建立新評論")

    def put(self, request):
        return HttpResponse("修改評論")

    def delete(self, request):
        return HttpResponse("刪除評論")

代碼看着,就很清晰了。知道哪種請求方式,該作哪些操做

APIView

以前學習django用的都是View,APIView它是什麼呢?

APIView與View的區別

APIView是View的子類
傳遞給請求處理程序的request實例是REST框架的請求實例,而不是Django的HttpRequest實例

處理程序返回的基於REST框架的Response,而不是Django的HttpResponse,視圖函數將會管理內容協商,而後設置正確的渲染方式

任何APIException將會被捕捉,而後轉換成合適的response對象

接收到的請求首先被認證,而後賦予相應的權限,而後經過節流器分發給相應的請求處理函數,相似.get()和.post() 

APIView是專門寫API的視圖函數,結合serializers,很是方便作序列化!

關於APIView的源碼解析,請參考文章:

https://blog.csdn.net/u013210620/article/details/79857654

 

APIView的請求書,都在data裏面,

可是對於GET,參數在self.request.query_params裏面  

修改views.py,完整代碼以下:

 1 from django.shortcuts import render,HttpResponse
 2 from django.http import JsonResponse
 3 from app01 import models
 4 import json
 5 from rest_framework import serializers
 6 from rest_framework.views import APIView
 7 
 8 
 9 # Create your views here.
10 #第二種方式
11 # def article_list(request):
12 #     #去數據庫查詢全部的文章數據,返回queryset,每個元素都是字典
13 #     query_set = models.Article.objects.all().values("id","title","create_time","type","school")
14 #     print(query_set)
15 #     for i in query_set:
16 #         print(i)
17 #
18 #         #學校對象
19 #         school_obj = models.School.objects.filter(id=i['school']).first()
20 #         #學校id
21 #         id = school_obj.id
22 #         #學校的名字
23 #         name = school_obj.name
24 #         #修改字典,key爲school的值
25 #         i['school'] = {"id":id,"name":name}
26 #
27 #         #返回json對象,safe=False表示任何能轉換爲json格式的對象
28 #         return JsonResponse(list(query_set),safe=False)
29 #
30 
31 
32 class DBG(serializers.Serializer):  # 聲明序列化器
33     id = serializers.IntegerField()
34     title = serializers.CharField()
35     create_time = serializers.DateField()
36     type = serializers.IntegerField()
37     school = serializers.CharField(source="school.name")
38 
39 
40 class CYM(serializers.ModelSerializer):  # 聲明ModelSerializer
41     #
42     type = serializers.CharField(source='get_type_display')
43 
44     class Meta:
45         model = models.Article
46         fields = "__all__"  # ("id", "title", "type")
47         depth = 1  # 官方推薦不超過10層
48 
49 def article_list(request):  # 查詢全部
50     # 去數據庫查詢全部的文章數據
51     query_set = models.Article.objects.all()
52     xbg = CYM(query_set, many=True)
53     print(xbg.data)
54     # 返回
55     return JsonResponse(xbg.data, safe=False)
56 
57 def article_detail(request,id): #查詢單條數據
58     article_obj = models.objects.filter(id=id).first()
59     xcym = CYM(article_obj)
60     return JsonResponse(xcym.data)
61 
62 class Comment(APIView):
63     def get(self,request):
64         print(request)
65         print(self.request.query_params)    #get請求參數
66         print(self.request.data)            #其餘請求方式參數,好比POST,PUT
67         return HttpResponse("獲取評論")
68 
69     def post(self,request):
70         print(self.request.query_params)    #get請求參數
71         print(self.request.data)        #其餘請求方式參數,好比:post,put
72         return HttpResponse("建立新評論")
73 
74     def put(self,request):
75         return HttpResponse("修改評論")
76 
77     def delate(self,request):
78         return HttpResponse("刪除評論")
79     
Views.py

get請求

舉例:get方式

點擊Parsms設置參數age爲18,那麼url會自動變成http://127.0.0.1:8000/api/comment/?age=18

點擊send,結果以下:

查看Pycharm控制檯輸出:

<rest_framework.request.Request object at 0x0000027A15ED9E80>
<QueryDict: {'age': ['18']}>
{}

 

post請求

post請求參數,是在請求體中的,點擊Body,默認數據格式爲form-data,也就是表單提交

點擊send,效果以下:

查看Pycharm控制檯輸出:

<QueryDict: {}>
<QueryDict: {'age': ['18']}>

 

能夠發現get參數是空的,可是post是有參數的!

注意:針對提交類型不一樣,獲取參數也是不一樣的!不必定就在self.request.data裏面,詳情請參考:

https://www.jianshu.com/p/f2f73c426623

 

3、DRF 序列化

DRF是django rest framework的簡稱

表結構:

仍是以about_drf項目爲例,增長几個表,models.py完整代碼以下:

仍是以about_drf項目爲例,增長几個表,models.py完整代碼以下:

from django.db import models

# Create your models here.


# 文章表
class Article(models.Model):
title = models.CharField(max_length=32, unique=True, error_messages={"unique": "文章標題不能重複"})
# 文章發佈時間
# auto_now每次更新的時候會把當前時間保存
create_time = models.DateField(auto_now_add=True)
# auto_now_add 第一次建立的時候把當前時間保存
update_time = models.DateField(auto_now=True)
# 文章的類型
type = models.SmallIntegerField(
choices=((1, "原創"), (2, "轉載")),
default=1
)
# 來源
school = models.ForeignKey(to='School', on_delete=models.CASCADE)
# 標籤
tag = models.ManyToManyField(to='Tag')


# 文章來源表
class School(models.Model):
name = models.CharField(max_length=16)


# 文章標籤表
class Tag(models.Model):
name = models.CharField(max_length=16)


# 評論表
class Comment(models.Model):
content = models.CharField(max_length=128)
article = models.ForeignKey(to='Article', on_delete=models.CASCADE)


複製代碼
from django.db import models

# Create your models here.


# 文章表
class Article(models.Model):
    title = models.CharField(max_length=32, unique=True, error_messages={"unique": "文章標題不能重複"})
    # 文章發佈時間
    # auto_now每次更新的時候會把當前時間保存
    create_time = models.DateField(auto_now_add=True)
    # auto_now_add 第一次建立的時候把當前時間保存
    update_time = models.DateField(auto_now=True)
    # 文章的類型
    type = models.SmallIntegerField(
        choices=((1, "原創"), (2, "轉載")),
        default=1
    )
    # 來源
    school = models.ForeignKey(to='School', on_delete=models.CASCADE)
    # 標籤
    tag = models.ManyToManyField(to='Tag')


# 文章來源表
class School(models.Model):
    name = models.CharField(max_length=16)


# 文章標籤表
class Tag(models.Model):
    name = models.CharField(max_length=16)


# 評論表
class Comment(models.Model):
    content = models.CharField(max_length=128)
    article = models.ForeignKey(to='Article', on_delete=models.CASCADE)
複製代碼

使用2個命令,生成表

python manage.py makemigrations
python manage.py migrate

使用navicat打開sqlite3數據庫,增長几條數據

# 評論表
INSERT INTO app01_comment ("id", "content", "article_id") VALUES (1, '呵呵', 1);
INSERT INTO app01_comment ("id", "content", "article_id") VALUES (2, '哈哈', 2);
INSERT INTO app01_comment ("id", "content", "article_id") VALUES (3, '嘿嘿', 3);
INSERT INTO app01_comment ("id", "content", "article_id") VALUES (4, '嘻嘻', 2);
INSERT INTO app01_comment ("id", "content", "article_id") VALUES (5, '嗚嗚', 1);

# 文章表
INSERT INTO app01_article ("id", "create_time", "update_time", "type", "school_id", "title") VALUES (4, '2018-08-01', '2018-08-01', 1, 1, 'Linux全都是記不住的命令');

# 文章和標籤關係表
INSERT INTO app01_article_tag ("id", "article_id", "tag_id") VALUES (3, 2, 1);
INSERT INTO app01_article_tag ("id", "article_id", "tag_id") VALUES (4, 4, 1);
INSERT INTO app01_article_tag ("id", "article_id", "tag_id") VALUES (5, 4, 2);
INSERT INTO app01_article_tag ("id", "article_id", "tag_id") VALUES (6, 4, 3);

複製代碼
# 評論表
INSERT INTO app01_comment ("id", "content", "article_id") VALUES (1, '呵呵', 1);
INSERT INTO app01_comment ("id", "content", "article_id") VALUES (2, '哈哈', 2);
INSERT INTO app01_comment ("id", "content", "article_id") VALUES (3, '嘿嘿', 3);
INSERT INTO app01_comment ("id", "content", "article_id") VALUES (4, '嘻嘻', 2);
INSERT INTO app01_comment ("id", "content", "article_id") VALUES (5, '嗚嗚', 1);

# 文章表
INSERT INTO app01_article ("id", "create_time", "update_time", "type", "school_id", "title") VALUES (4, '2018-08-01', '2018-08-01', 1, 1, 'Linux全都是記不住的命令');

# 文章和標籤關係表
INSERT INTO app01_article_tag ("id", "article_id", "tag_id") VALUES (3, 2, 1);
INSERT INTO app01_article_tag ("id", "article_id", "tag_id") VALUES (4, 4, 1);
INSERT INTO app01_article_tag ("id", "article_id", "tag_id") VALUES (5, 4, 2);
INSERT INTO app01_article_tag ("id", "article_id", "tag_id") VALUES (6, 4, 3);
複製代碼

 

查看評論表記錄

查看文章表記錄

查看文章和標籤關係表

 

單表的GET和POST:

使用serializers序列化,針對每個表,須要單獨寫函數。通常會寫在views.py裏面,可是這樣作,會致使整個文件代碼過長。須要分離出來!

在app01(應用名)目錄下,建立文件app01_serialization.py,表示自定義序列化

使用serializers序列化,針對每個表,須要單獨寫函數。通常會寫在views.py裏面,可是這樣作,會致使整個文件代碼過長。須要分離出來!

在app01(應用名)目錄下,建立文件app01_serialization.py,表示自定義序列化

from app01 import models
from rest_framework import serializers

# 序列化評論的類
class CommentSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Comment  # Comment表
        fields = "__all__"  # 序列化全部字段
        depth = 2  # 深度爲2

複製代碼
from app01 import models
from rest_framework import serializers

# 序列化評論的類
class CommentSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Comment  # Comment表
        fields = "__all__"  # 序列化全部字段
        depth = 2  # 深度爲2
複製代碼

修改app01_urls.py,註釋掉多餘的路徑

from django.conf.urls import url
from app01 import views

urlpatterns = [
# url(r'^article_list/', views.article_list),
# url(r'article_detail/(\d+)', views.article_detail),

# 評論
url(r'comment/', views.Comment.as_view()),
]

複製代碼
from django.conf.urls import url
from app01 import views

urlpatterns = [
    # url(r'^article_list/', views.article_list),
    # url(r'article_detail/(\d+)', views.article_detail),

    # 評論
    url(r'comment/', views.Comment.as_view()),
]
複製代碼

修改views.py,刪除多餘的代碼,並使用app01_serialization

from django.shortcuts import render,HttpResponse
from django.http import JsonResponse
from app01 import models
import json
from rest_framework import serializers
from django import views
from rest_framework.views import APIView
from app01 import app01_serialization # 導入自定義的序列化

# Create your views here.

class Comment(APIView):
def get(self, request):
res = {"code":0} # 默認狀態
all_comment = models.Comment.objects.all()
# print(all_comment)
# 序列化,many=True表示返回多條
ser_obj = app01_serialization.CommentSerializer(all_comment, many=True)
res["data"] = ser_obj.data

return JsonResponse(res)

def post(self, request):
return HttpResponse("建立新評論")

def put(self, request):
return HttpResponse("修改評論")

def delete(self, request):
return HttpResponse("刪除評論")


複製代碼
from django.shortcuts import render,HttpResponse
from django.http import JsonResponse
from app01 import models
import json
from rest_framework import serializers
from django import views
from rest_framework.views import APIView
from app01 import app01_serialization  # 導入自定義的序列化

# Create your views here.

class Comment(APIView):
    def get(self, request):
        res = {"code":0}  # 默認狀態
        all_comment = models.Comment.objects.all()
        # print(all_comment)
        # 序列化,many=True表示返回多條
        ser_obj = app01_serialization.CommentSerializer(all_comment, many=True)
        res["data"] = ser_obj.data

        return JsonResponse(res)

    def post(self, request):
        return HttpResponse("建立新評論")

    def put(self, request):
        return HttpResponse("修改評論")

    def delete(self, request):
        return HttpResponse("刪除評論")
複製代碼

使用postman發送get請求,不帶參數。效果以下:

Response

 Rest framework 引入了Response 對象,它是一個TemplateResponse類型,並根據客戶端需求正確返回須要的類型。

使用前,須要導入模塊

from rest_framework.response import Response

語法:

return Response(data)    #根據客戶端的需求返回不一樣的類型

舉例:

修改視圖Comment中的get方法,將JsonResponse改爲Response

 1 from django.shortcuts import render,HttpResponse
 2 from django.http import JsonResponse
 3 from app01 import models
 4 import json
 5 from rest_framework import serializers
 6 from django import views
 7 from rest_framework.views import APIView
 8 from app01 import app01_serializers  # 導入自定義的序列化
 9 from rest_framework.response import Response
10 
11 # Create your views here.
12 
13 class Comment(APIView):
14     def get(self, request):
15         res = {"code":0}  # 默認狀態
16         all_comment = models.Comment.objects.all()
17         # print(all_comment)
18         # 序列化,many=True表示返回多條
19         ser_obj = app01_serializers.CommentSerializer(all_comment, many=True)
20         res["data"] = ser_obj.data
21 
22         return Response(res)
23 
24     def post(self, request):
25         return HttpResponse("建立新評論")
26 
27     def put(self, request):
28         return HttpResponse("修改評論")
29 
30     def delete(self, request):
31         return HttpResponse("刪除評論")
Views

使用瀏覽器訪問url:  http://127.0.0.1:8000/api/comment/

出現錯誤,找不到模板文件

 

注意:使用postman,訪問get請求,是不會報錯的。可是使用瀏覽器訪問,會報錯。由於瀏覽器要渲染頁面!

 

修改settings.py,在INSTALLED_APPS配置項中,最後一行添加rest_framework

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'app01.apps.App01Config',
    'rest_framework',
]

刷新頁面,效果以下:

這個頁面,能夠發送post請求

 

serializers校驗

以前學到了form組件校驗,那麼serializers也能夠校驗。

校驗空數據

舉例:判斷空數據

修改views.py,添加post邏輯代碼。注意:使用is_valid校驗

 1 from django.shortcuts import render,HttpResponse
 2 from django.http import JsonResponse
 3 from app01 import models
 4 import json
 5 from rest_framework import serializers
 6 from django import views
 7 from rest_framework.views import APIView
 8 from app01 import app01_serialization  # 導入自定義的序列化
 9 from rest_framework.response import Response
10 
11 # Create your views here.
12 
13 class Comment(APIView):
14     def get(self, request):
15         res = {"code":0}  # 默認狀態
16         all_comment = models.Comment.objects.all()
17         # print(all_comment)
18         # 序列化,many=True表示返回多條
19         ser_obj = app01_serialization.CommentSerializer(all_comment, many=True)
20         res["data"] = ser_obj.data
21 
22         return Response(res)
23 
24     def post(self, request):
25         res = {"code": 0}
26         # 去提交的數據
27         comment_data = self.request.data
28         # 對用戶提交的數據作校驗
29         ser_obj = app01_serialization.CommentSerializer(data=comment_data)
30         if ser_obj.is_valid():
31             # 表示數據沒問題,能夠建立
32             pass
33         else:
34             # 表示數據有問題
35             res["code"] = 1
36             res["error"] = ser_obj.errors
37         return Response(res)
38 
39         # return HttpResponse("建立新評論")
40 
41     def put(self, request):
42         return HttpResponse("修改評論")
43 
44     def delete(self, request):
45         return HttpResponse("刪除評論")
Views

 

使用postman發送一個空數據的post請求

它返回This field is required,表示次字段不能爲空!

 

它返回This field is required,表示次字段不能爲空!

 

錯誤信息中文顯示

修改app01_serializers.py,使用extra_kwargs指定錯誤信息

重啓django,從新發送空的post請求

發送一個帶參數的post請求,注意key爲content

code爲0表示成功了!看一下app01_comment表記錄,發現並無增長數據?爲何呢?由於成功時,執行了pass,並無執行插入操做!

外鍵的GET和POST

序列化校驗

上面雖然只發送了content參數,就讓經過了,顯然不合理!爲何呢?

由於app01_comment表有2個字段,content和article。這2個字段都應該校驗纔對!

由於serializers默認校驗時,排除了外鍵字段。好比article

要對外鍵進行校驗,必須在extra_kwargs中指定外鍵字段

修改app01_serializers.py,注意關閉depth參數

當序列化類MATE中定義了depth時,這個序列化類中引用字段(外鍵)則自動變爲只讀,因此進行更新或者建立操做的時候不能使用此序列化類

大概意思就是,使用了depth參數,會忽略外鍵字段

完整代碼以下:

from app01 import models
from rest_framework import serializers

# 序列化評論的類
class CommentSerializer(serializers.ModelSerializer):
class Meta:
model = models.Comment # Comment表
fields = "__all__" # 序列化全部字段
# depth = 2 # 深度爲2
# 定義額外的參數
extra_kwargs = {
"content": {
"error_messages": {
"required": '內容不能爲空',
}
},
"article": {
"error_messages": {
"required": '文章不能爲空'
}
}
}


複製代碼
from app01 import models
from rest_framework import serializers

# 序列化評論的類
class CommentSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Comment  # Comment表
        fields = "__all__"  # 序列化全部字段
        # depth = 2  # 深度爲2
        # 定義額外的參數
        extra_kwargs = {
            "content": {
                "error_messages": {
                    "required": '內容不能爲空',
                }
            },
            "article": {
                "error_messages": {
                    "required": '文章不能爲空'
                }
            }
        }
複製代碼

再次發送post請求,仍是隻有一個參數content

查看執行結果:

 發送正確的2個參數

查看結果

 

read_only=True

 read_only:True表示不容許用戶本身上傳,只能用於api的輸出。若是某個字段設置了read_only=True,那麼就不須要進行數據驗證,只會在返回時,將這個字段序列化後返回

 舉例:容許article不校驗

修改app01_serializers.py,加入一行代碼

article = serializers.SerializerMethodField(read_only=True)

完整代碼以下:

from app01 import models
from rest_framework import serializers

# 序列化評論的類
class CommentSerializer(serializers.ModelSerializer):
    article = serializers.SerializerMethodField(read_only=True)

    class Meta:
        model = models.Comment  # Comment表
        fields = "__all__"  # 序列化全部字段
        # depth = 2  # 深度爲2
        # 定義額外的參數
        extra_kwargs = {
            "content": {
                "error_messages": {
                    "required": '內容不能爲空',
                }
            },
            "article": {
                "error_messages": {
                    "required": '文章不能爲空'
                }
            }
        }

只發content參數

查看結果,發現經過了!

這個參數在什麼場景下,使用呢?

舉個簡單的例子:在用戶進行購物的時候,用戶post訂單時,確定會產生一個訂單號,而這個訂單號應該由後臺邏輯完成,而不該該由用戶post過來,若是不設置read_only=True,那麼驗證的時候就會報錯 

 

保存post數據

修改views.py,在post方法中,將pass改爲ser_obj.save(),完整代碼以下:

from django.shortcuts import render,HttpResponse
from django.http import JsonResponse
from app01 import models
import json
from rest_framework import serializers
from django import views
from rest_framework.views import APIView
from app01 import app01_serializers  # 導入自定義的序列化
from rest_framework.response import Response

# Create your views here.

class Comment(APIView):
    def get(self, request):
        res = {"code":0}  # 默認狀態
        all_comment = models.Comment.objects.all()
        # print(all_comment)
        # 序列化,many=True表示返回多條
        ser_obj = app01_serializers.CommentSerializer(all_comment, many=True)
        res["data"] = ser_obj.data

        return Response(res)

    def post(self, request):
        res = {"code": 0}
        # 去提交的數據
        comment_data = self.request.data
        # 對用戶提交的數據作校驗
        ser_obj = app01_serializers.CommentSerializer(data=comment_data)
        if ser_obj.is_valid():
            # 表示數據沒問題,能夠建立
            ser_obj.save()
        else:
            # 表示數據有問題
            res["code"] = 1
            res["error"] = ser_obj.errors
        return Response(res)

        # return HttpResponse("建立新評論")

    def put(self, request):
        return HttpResponse("修改評論")

    def delete(self, request):
        return HttpResponse("刪除評論")
Views

修改app01_serializers.py,註釋掉read_only=True

from app01 import models
from rest_framework import serializers

# 序列化評論的類
class CommentSerializer(serializers.ModelSerializer):
    # article = serializers.SerializerMethodField(read_only=True)

    class Meta:
        model = models.Comment  # Comment表
        fields = "__all__"  # 序列化全部字段
        # depth = 2  # 深度爲2
        # 定義額外的參數
        extra_kwargs = {
            "content": {
                "error_messages": {
                    "required": '內容不能爲空',
                }
            },
            "article": {
                "error_messages": {
                    "required": '文章不能爲空'
                }
            }
        }

發送2個正確的參數

查看返回結果

查看app01_comment表記錄,發現多了一條記錄

爲何直接save,就能夠保存了呢?

由於它將校驗過的數據傳過去了,就好像form組件中的self.cleaned_data同樣

 

非serializer 的驗證條件

好比重置密碼、修改密碼都須要手機驗證碼。可是用戶 model 裏面並無驗證碼這個選項

須要使用validate,用於作校驗的鉤子函數,相似於form組件的clean_字段名

使用時,須要導入模塊,用來輸出錯誤信息

from rest_framework.validators import ValidationError

局部鉤子

validate_字段名,表示局部鉤子。

舉例:評論的內容中,不能包含 "草"

修改app01_serializers.py,校驗評論內容

from app01 import models
from rest_framework import serializers
from rest_framework.validators import ValidationError

# 序列化評論的類
class CommentSerializer(serializers.ModelSerializer):
    # article = serializers.SerializerMethodField(read_only=True)

    # 用於作校驗的鉤子函數,相似於form組件的clean_字段名
    def validate_content(self, value):
        if '草' in value:
            raise ValidationError('不符合社會主義核心價值觀!')
        else:
            return value

    class Meta:
        model = models.Comment  # Comment表
        fields = "__all__"  # 序列化全部字段
        # depth = 2  # 深度爲2
        # 定義額外的參數
        extra_kwargs = {
            "content": {
                "error_messages": {
                    "required": '內容不能爲空',
                }
            },
            "article": {
                "error_messages": {
                    "required": '文章不能爲空'
                }
            }
        }

使用postman發送包含關鍵字的評論

查看返回結果:

全局鉤子

validate,表示全局鉤子。

好比在用戶註冊時,咱們須要填寫驗證碼,這個驗證碼只須要驗證,不須要保存到用戶這個Model中:

def validate(self, attrs):
        del attrs["code"]
        return attrs

這裏就不演示了,about_drf項目尚未驗證碼功能!

 

多對多的GET和POST

路由

修改app01_urls.py,增長路由

from django.conf.urls import url
from app01 import views

urlpatterns = [
    # url(r'^article_list/', views.article_list),
    # url(r'article_detail/(\d+)', views.article_detail),

    #文章
    url(r'article/',views.APIView.as_view()),

    # 評論
    url(r'comment/', views.Comment.as_view()),
]

GET

修改views.py, 增長視圖函數

from django.shortcuts import render,HttpResponse
from django.http import JsonResponse
from app01 import models
import json
from rest_framework import serializers
from django import views
from rest_framework.views import APIView
from app01 import app01_serializers  # 導入自定義的序列化
from rest_framework.response import Response

# Create your views here.

class Comment(APIView):
    def get(self, request):
        res = {"code":0}  # 默認狀態
        all_comment = models.Comment.objects.all()
        # print(all_comment)
        # 序列化,many=True表示返回多條
        ser_obj = app01_serializers.CommentSerializer(all_comment, many=True)
        res["data"] = ser_obj.data

        return Response(res)

    def post(self, request):
        res = {"code": 0}
        # 去提交的數據
        comment_data = self.request.data
        # 對用戶提交的數據作校驗
        ser_obj = app01_serializers.CommentSerializer(data=comment_data)
        if ser_obj.is_valid():
            # 表示數據沒問題,能夠建立
            ser_obj.save()
        else:
            # 表示數據有問題
            res["code"] = 1
            res["error"] = ser_obj.errors
        return Response(res)

        # return HttpResponse("建立新評論")

    def put(self, request):
        return HttpResponse("修改評論")

    def delete(self, request):
        return HttpResponse("刪除評論")

# 文章CBV
class Article(APIView):
    def get(self, request):
        res = {"code": 0}
        all_article = models.Article.objects.all()
        ser_obj = app01_serializers.ArticleModelSerializer(all_article, many=True)
        res["data"] = ser_obj.data
        return Response(res)

修改app01_serializers.py,增長文章的序列化類

from app01 import models
from rest_framework import serializers
from rest_framework.validators import ValidationError

# 序列化評論的類
class CommentSerializer(serializers.ModelSerializer):
    # article = serializers.SerializerMethodField(read_only=True)

    # 用於作校驗的鉤子函數,相似於form組件的clean_字段名
    def validate_content(self, value):
        if '草' in value:
            raise ValidationError('不符合社會主義核心價值觀!')
        else:
            return value

    #全局的鉤子
    def validate(self, attrs):
        # self.validated_data  # 通過校驗的數據 相似於form組件中的cleaned_data
        # 全局鉤子
        pass

    class Meta:
        model = models.Comment  # Comment表
        fields = "__all__"  # 序列化全部字段
        # depth = 2  # 深度爲2
        # 定義額外的參數
        extra_kwargs = {
            "content": {
                "error_messages": {
                    "required": '內容不能爲空',
                }
            },
            "article": {
                "error_messages": {
                    "required": '文章不能爲空'
                }
            }
        }
        
# 文章的序列化類
class ArticleModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Article  # 綁定的ORM類是哪個
        fields = "__all__"  # ["id", "title", "type"]
        # depth = 1  # 官方推薦不超過10層

  發送get請求

POST

修改views.py,增長post

from django.shortcuts import render,HttpResponse
from django.http import JsonResponse
from app01 import models
import json
from rest_framework import serializers
from django import views
from rest_framework.views import APIView
from app01 import app01_serialization  # 導入自定義的序列化
from rest_framework.response import Response

# Create your views here.

class Comment(APIView):
    def get(self, request):
        res = {"code":0}  # 默認狀態
        all_comment = models.Comment.objects.all()
        # print(all_comment)
        # 序列化,many=True表示返回多條
        ser_obj = app01_serialization.CommentSerializer(all_comment, many=True)
        res["data"] = ser_obj.data

        return Response(res)

    def post(self, request):
        res = {"code": 0}
        # 去提交的數據
        comment_data = self.request.data
        # 對用戶提交的數據作校驗
        ser_obj = app01_serialization.CommentSerializer(data=comment_data)
        if ser_obj.is_valid():
            # 表示數據沒問題,能夠建立
            ser_obj.save()
        else:
            # 表示數據有問題
            res["code"] = 1
            res["error"] = ser_obj.errors
        return Response(res)

        # return HttpResponse("建立新評論")

    def put(self, request):
        return HttpResponse("修改評論")

    def delete(self, request):
        return HttpResponse("刪除評論")

#文章CBV
class Article(APIView):
    def get(self,request):
        res = {"code":0}
        all_article = models.Article.objects.all()
        ser_obj = app01_serialization.ValidationError(all_article,many=True)
        res["data"] = ser_obj.data
        return Response(res)

    def post(self,request):
        res = {"code":0}
        ser_obj = app01_serialization.ArticleModelSerializer(data=self.request.data)
        if ser_obj.is_valid():
            ser_obj.save()
        else:
            res["code"] = 1
            res["error"] = ser_obj.errors
        return Response(res)

發送post請求,增長參數type

查看結果

提示不是一個有效的選項,爲何呢?由於article表的type字段的值,只有1和2。不能是其餘的值!

 

增長正確的值

先使用postman發送get請求,而後複製一段json數據

再使用postman發送post請求,參數以下

{
    "type":1,
    "title": "Python是世界上最好的語言",
    "create_time": "2018-08-01",
    "update_time": "2018-08-01",
    "school":1,
    "tag":[
        1,
        2,
        3
    ]
}

注意參數類型爲raw,表示未通過加工的。選擇json格式

查看返回狀態,提示成功了

查看app01_article表,發現多了一條記錄

 

惟一校驗

article表的titie加了惟一屬性,在添加時,須要判斷標題是否存在。

那麼serializers是不能作惟一校驗的,爲何呢?

針對這個問題,有人向官方提問了,官方回覆,ORM能夠作,這裏就沒有必要作了!

從新提交上面的post請求,結果輸出:

 

這個中文提示,是在ORM的error_messages參數定義的

title = models.CharField(max_length=32, unique=True, error_messages={"unique": "文章標題不能重複"})

它還能夠定義其餘錯誤,好比長度,爲空,惟一...

這個中文提示,是在ORM的error_messages參數定義的

title = models.CharField(max_length=32, unique=True, error_messages={"unique": "文章標題不能重複"})

它還能夠定義其餘錯誤,好比長度,爲空,惟一...

需求:要求api返回結果中,school展現的是超連接

路由

修改app01_urls.py,增長路由

urlpatterns = [
    # 文章
    url(r'article/', views.Article.as_view()),
    url(r'article/(?P<pk>\d+)', views.ArticleDetail.as_view(), name='article-detail'),
    # 學校
    url(r'school/(?P<id>\d+)', views.SchoolDetail.as_view(), name='school-detail'),
    # 評論
    url(r'comment/', views.Comment.as_view()),
]

指定name是爲作反向連接,它能解析出絕對url

序列化

修改app01_serializers.py

from app01 import models
from rest_framework import serializers
from rest_framework.validators import ValidationError

# 序列化評論的類
class CommentSerializer(serializers.ModelSerializer):
    # article = serializers.SerializerMethodField(read_only=True)

    # 用於作校驗的鉤子函數,相似於form組件的clean_字段名
    def validate_content(self, value):
        if '' in value:
            raise ValidationError('不符合社會主義核心價值觀!')
        else:
            return value

    #全局的鉤子
    def validate(self, attrs):
        #self.validate_data #通過校驗的數據 相似於form組件中的cleaned_data
        #全局鉤子
        pass

    class Meta:
        model = models.Comment  # Comment表
        fields = "__all__"  # 序列化全部字段
        # depth = 2  # 深度爲2
        # 定義額外的參數
        extra_kwargs = {
            "content": {
                "error_messages": {
                    "required": '內容不能爲空',
                }
            },
            "article": {
                "error_messages": {
                    "required": '文章不能爲空'
                }
            }
        }

#文章的序列化類
class ArticleModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Article  #綁定的ORM類是哪個
        fields = "__all__"

#學校的序列化
class SchoolSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.School
        fields = "__all__"
Views.py

參數解釋:

source 表示來源

lookup_field 表示查找字段

lookup_url_kwarg 表示路由查找的參數,pk表示主鍵

view_name  它是指urls定義的name值,必定要一一對應。

 

修改views.py,增長視圖函數

from django.shortcuts import render,HttpResponse
from django.http import JsonResponse
from app01 import models
import json
from rest_framework import serializers
from django import views
from rest_framework.views import APIView
from app01 import app01_serializers  # 導入自定義的序列化
from rest_framework.response import Response

# Create your views here.

class Comment(APIView):
    def get(self, request):
        res = {"code":0}  # 默認狀態
        all_comment = models.Comment.objects.all()
        # print(all_comment)
        # 序列化,many=True表示返回多條
        ser_obj = app01_serializers.CommentSerializer(all_comment, many=True)
        res["data"] = ser_obj.data

        return Response(res)

    def post(self, request):
        res = {"code": 0}
        # 去提交的數據
        comment_data = self.request.data
        # 對用戶提交的數據作校驗
        ser_obj = app01_serializers.CommentSerializer(data=comment_data)
        if ser_obj.is_valid():
            # 表示數據沒問題,能夠建立
            ser_obj.save()
        else:
            # 表示數據有問題
            res["code"] = 1
            res["error"] = ser_obj.errors
        return Response(res)

        # return HttpResponse("建立新評論")

    def put(self, request):
        return HttpResponse("修改評論")

    def delete(self, request):
        return HttpResponse("刪除評論")

# 文章CBV
class Article(APIView):
    def get(self, request):
        res = {"code": 0}
        all_article = models.Article.objects.all()
        ser_obj = app01_serializers.ArticleHyperLinkedSerializer(all_article, many=True, context={'request': request})
        res["data"] = ser_obj.data
        return Response(res)

    def post(self, request):
        res = {"code": 0}
        ser_obj = app01_serializers.ArticleModelSerializer(data=self.request.data)
        if ser_obj.is_valid():
            ser_obj.save()
        else:
            res["code"] = 1
            res["error"] = ser_obj.errors
        return Response(res)


# 文章詳情CBV
class ArticleDetail(APIView):
    def get(self, request, pk):
        res = {"code": 0}
        article_obj = models.Article.objects.filter(pk=pk).first()
        # 序列化
        ser_obj = app01_serializers.ArticleHyperLinkedSerializer(article_obj, context={'request': request})
        res["data"] = ser_obj.data
        return Response(res)


# 學校詳情CBV
class SchoolDetail(APIView):
    def get(self, request, id):
        res = {"code": 0}
        school_obj = models.School.objects.filter(pk=id).first()
        ser_obj = app01_serializers.SchoolSerializer(school_obj, context={'request': request})
        res["data"] = ser_obj.data
        return Response(res)
Views.py

參數解釋: 

id 表示參數,它和url的參數,是一一對應的

content 表示上下文

 

重啓django項目,訪問網頁:

http://127.0.0.1:8000/api/article/1

效果以下:

點擊第一個連接,效果以下:

 

今日內容總結:

1. RESTful風格API介紹
2. from rest_framework import APIView


class Comment(views.View):
    pass
    
--------  以前的寫法 ↑  ----------
    
class APIView(views.View):
    擴展的功能
    self.request = Resquest(
        self._request = request(Django的request)
    )
    self.request.data  封裝好的數據屬性,POST\PUT請求攜帶的數據均可以從這裏取
    pass


class Comment(APIView):
    pass
    
    
3. serializer
    1. ModelSerializer
        1. 基於APIView實現的GET和POST
        2. POST過來的數據進行校驗
        3. 保存數據
    2. 超連接的URL
相關文章
相關標籤/搜索