程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式師世界 >> 編程語言 >  >> 更多編程語言 >> Python

Django Rest Framework 框架

編輯:Python

學習地址:Django從入門到放棄 - 劉清政 - 博客園

第一階段:

一、什麼是Rest Framework?

    REST與技術無關,代表的是一種軟件架構風格,REST是Representational State Transfer的簡稱,中文翻譯為“表征狀態轉移”
    REST從資源的角度類審視整個網絡,它將分布在網絡中某個節點的資源通過URL進行標識,客戶端應用通過URL來獲取資源的表征,獲得這些表征致使這些應用轉變狀態
    所有的數據,不過是通過網絡獲取的還是操作(增刪改查)的數據,都是資源,將一切數據視為資源是REST區別與其他架構風格的最本質屬性
    對於REST這種面向資源的架構風格,有人提出一種全新的結構理念,即:面向資源架構(ROA:Resource Oriented Architecture)

二、Rest Framework API設計規范-10條

1、API與用戶的通信協議,總是使用HTTPs協議。

2、域名 
  https://api.example.com                         盡量將API部署在專用域名(會存在跨域問題)
  https://example.org/api/                        API很簡單

3、版本
  URL,如:https://api.example.com/v1/
  請求頭                                                  跨域時,引發發送多次請求

4、路徑,視網絡上任何東西都是資源,均使用名詞表示(可復數)
  https://api.example.com/v1/zoos
  https://api.example.com/v1/animals
  https://api.example.com/v1/employees

5、method
  GET      :從服務器獲取資源(一項或多項)
  POST    :在服務器新增一個資源 (提交一個數據)
  PUT      :在服務器更新資源(客戶端提供改變後的完整資源)
  PATCH  :在服務器更新資源(客戶端提供改變的屬性)
  DELETE :從服務器刪除資源

6、過濾,通過在url上傳參的形式傳遞搜索條件,通過request.GET.get('key')的方式獲取值
  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:指定篩選條件

7、狀態碼
  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://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html

8、錯誤處理,應返回錯誤信息,error當做key。
{
    error: "Invalid API key"
}

9、返回結果,針對不同操作,服務器向用戶返回的結果應該符合以下規范。
  GET /collection:返回所有資源對象,一個列表格式
  GET /collection/resource:返回單個資源對象
  POST /collection:返回新生成的資源對象
  PUT /collection/resource:返回完整的資源對象
  PATCH /collection/resource:返回完整的資源對象
  DELETE /collection/resource:返回一個空文檔

10、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"
}}

第二階段:

三、安裝django rest framework

   方式一:pip3 install djangorestframework
   方式二:pycharm圖形化界面安裝
   方式三:pycharm命令行下安裝(裝在當前工程所用的解釋器下)

   使用:基於django rest framework 的接口,均使用CBV來寫方法。

四、django rest framework 的APIView分析,這裡參考前面的CBV源碼分析

      as_view 來自APIView類裡的as_view方法,同時APIView類的dispatch方法("def dispatch(self, request, *args, **kwargs):")將request對象進行了一層封裝"request = self.initialize_request(request, *args, **kwargs)"。而真正封裝request對象方法的是"initialize_request"方法,如下:

 def initialize_request(self, request, *args, **kwargs):
"""
Returns the initial request object.
"""
parser_context = self.get_parser_context(request)
return Request(
request, # 原生的request
parsers=self.get_parsers(), # 解析器
authenticators=self.get_authenticators(), # 認證器
negotiator=self.get_content_negotiator(), # 分頁器
parser_context=parser_context # 響應器
)
# self.initial(request, *args, **kwargs) 包含的
self.perform_authentication(request) # 認證組件
self.check_permissions(request) # 權限組件
self.check_throttles(request) # 頻率組件

 經過封裝後的request,會重新賦值給 request = self.initialize_request(request, *args, **kwargs),那麼最終到達視圖函數的就是封裝後的request。

  4.1、如何拿到原生的request

  Request類裡面將request賦值給了self._request,那麼通過 self._request 就能拿到原生的request。對應到代碼裡面就是:request._request

  4.2、封裝後的request如何通過request.method拿到對應的值

  Request類裡面有一個"__getattr__"方法,原本需要通過request._request.method才能拿到值,現在通過request.method就可以拿到了。本質還是通過request._request.method(原生request)的方式。

 def __getattr__(self, attr):
try:
return getattr(self._request, attr)
except AttributeError:
return self.__getattribute__(attr)

  4.3、封裝後的request可以直接通過request.data的方式直接拿到數據,而不向django只能接收from-data和urlencoded,Json的格式還需要轉移才能接收。也就是說拿request的數據就直接通過request.data即可。

五、解析器

  5.1、解析器的作用

  根據請求頭 content-type 選擇對應的解析器對請求體內容進行處理。有application/json,x-www-form-urlencoded,form-data等格式。

  5.2、解析器全局配置和局部配置

  如果解析器只解析json格式,那麼其他格式請求過來就直接報錯415。

  局部配置寫到類裡面,全局配置的話,寫到setting.py文件裡面。全局和局部可以同時使用,一般全局用json,局部可以自定義使用多種。

局部配置
from rest_framework.views import APIView
from rest_framework.parsers import JSONParser # 導入模塊
class Index(APIView):
parser_classes = [JSONParser,] # 只解析json格式,局部配置
def get(self,request):
return HttpResponse("get")
def post(self,request):
return HttpResponse("post")
全局配置,寫到setting.py文件裡面
REST_FRAMEWORK = {
'DEFAULT_PARSER_CLASSES':[
'rest_framework.parsers.JSONParser' # 只解析json格式的請求
'rest_framework.parsers.FormParser' # 只解析x-www-form-urlencoded格式的請求
'rest_framework.parsers.MultiPartParser'
]
}

  非json格式的請求報錯如下:

   5.3、解析器的默認配置

   在 "rest_framework/setting.py"文件裡面,這個配置是rest_framework框架的默認配置(不是django的)。默認就這三個:

'DEFAULT_PARSER_CLASSES': [
        'rest_framework.parsers.JSONParser',   # 只解析json格式
        'rest_framework.parsers.FormParser',    # 只解析x-www-form-urlencoded格式
        'rest_framework.parsers.MultiPartParser'
    ]

   5.4、解析器的加載順序

  1.視圖函數內,2.django的setting.py文件內,3,."rest_framework/setting.py"文件內(默認配置)

六、認證組件

待研究

七、權限組件

待研究

第三階段:

八、序列化組件

8.1、原生方式寫接口以及返回數據(第一種方式)

from rest_framework.views import APIView
from app01.models import Book
from django.http import JsonResponse
class Books(APIView):
def get(self,request):
response={
"status":200,
"message":None,
}
books=Book.objects.all()
books_list=[{"name":n.name,"price":n.price,"publish_data":n.publish_date} for n in books]
response["message"]="success"
response["data"]=books_list
return JsonResponse(response,safe=False)
# 返回數據
# {
# "status": 200,
# "message": "success",
# "data": [
# {
# "name": "xiyouji",
# "price": "199.00"
# },
# {
# "name": "sanguoyanyi",
# "price": "299.00"
# }
# ]
# }

8.2、drf方式寫接口以及返回數據(第二種方式)

1.單獨定義某個類的序列化,這裡以Book類為例,那麼這個序列化只能解析Book類

# app01/my_serialzers.py
from rest_framework import serializers
class BookSerial(serializers.Serializer):
nid=serializers.IntegerField()
# name這個名稱就是數據庫內的名稱name,這樣寫不安全。
# 如果要指定別名:把name改為name3時,需要指定source='name'對應到name3這個別名。
name3=serializers.CharField(source='name')
price=serializers.CharField()
publish_date=serializers.CharField()
publish = serializers.CharField()
# 調用類下面的某一個屬性的值,通過source='publish.email'來傳值。
# source的功能就是調用類下面某一個屬性或者方法(前提是這個屬性或者方法必須存在)。
# 如果類(A)下面還是一個類(B)時,調用類(B)下面的某個方法只需要source='類(B).方法名'
publish_email = serializers.CharField(source='publish.email')
# source既可以傳屬性,也可以傳方法,source="屬性/方法"時,拿到的結果就是"屬性/方法"的返回值
class AuthorSerial(serializers.Serializer):
nid=serializers.IntegerField()
name=serializers.CharField()
age=serializers.IntegerField()
# author_detail = serializers.CharField() # 這裡拿到的數據是一個對象:"author_detail": "AuthorDatail object (1)"
author_telephone = serializers.CharField(source='author_detail.telephone')
author_birthday = serializers.CharField(source='author_detail.birthday')
class AuthorDatailSerial(serializers.Serializer):
nid = serializers.IntegerField()
telephone = serializers.IntegerField()
birthday = serializers.DateField()
addr = serializers.CharField()
class PublishSerial(serializers.Serializer):
nid = serializers.IntegerField()
name = serializers.CharField()
city = serializers.CharField()
email = serializers.EmailField()

2、單獨定義數據響應類,主要是定義數據返回的格式

# app01/my_response.py
class MyResponse():
def __init__(self):
self.status=200
self.msg=None
@property
def get_dic(self):
return self.__dict__
# 將類裡面的數據轉換成為字典 {"status":200,"msg":None}

3、視圖函數內使用

# 使用drf序列化組件的方式(第二種方式)
from rest_framework.views import APIView
from django.http import JsonResponse
from app01.models import Book
from app01 import my_serialzers
from app01 import my_response
class Books(APIView):
def get(self,request):
# 實例化response對象並賦值
response=my_response.MyResponse()
response.msg="success"
# 數據庫獲取數據
books=Book.objects.all()
res=my_serialzers.BookSerial(books,many=True) # many=True允許接收多條數據
# 將數據庫拿到的值,賦給response對象
response.data=res.data
# response.get_dic:把response對象的數據轉換為字典格式
return JsonResponse(response.get_dic,safe=False)
# 返回的數據
# {
# "status": 200,
# "msg": "success",
# "data": [
# {
# "nid": 1,
# "name3": "xiyouji",
# "price": "199.00",
# "publish_date": "1999-09-09",
# "publish": "北京出版社",
# "publish_email": "[email protected]"
# },
# {
# "nid": 2,
# "name3": "sanguoyanyi",
# "price": "299.00",
# "publish_date": "1993-04-05",
# "publish": "上海出版社",
# "publish_email": "[email protected]"
# }
# ]
# }

4、models.py文件內的配置

# 在models.py文件中
from django.db import models
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 __str__(self):
return self.name
class Author(models.Model):
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
age = models.IntegerField()
author_detail = models.OneToOneField(to='AuthorDatail',to_field='nid',unique=True,on_delete=models.CASCADE)
class AuthorDatail(models.Model):
nid = models.AutoField(primary_key=True)
telephone = models.BigIntegerField()
birthday = models.DateField()
addr = models.CharField(max_length=64)
class Publish(models.Model):
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
city = models.CharField(max_length=32)
email = models.EmailField()
def __str__(self): # 調用Publish類時,打印Publish.name
return self.name

5、總結:序列化組件之Serializer
  1、導入:from rest_framework import serializers
  2、寫一個類(myserial.py文件下,名字任意),繼承類serializers.Serializer
    BookSer(serializers.Serializer)
  3、如果不指定"source",字段名,那麼BookSer下的屬性名,必須跟數據庫列名一致。
  4、"source"既可以指定數據屬性(source='name')name為數據屬性,又可以指定方法屬性(source='test')test為類下的一個函數。
    如果類(A)下面還是一個類(B)時,調用類(B)下面的某個方法只需要source='類(B).方法名'
  5、序列化組件的使用:
    在views.py文件下,查出所有要使用的數據:books=models.Book.objects.all()
    在views.py文件下,序列化數據:res=myserial.BookSer(books,many=True)多條(queryset對象)  # many=True表示序列化多條數據
    在views.py文件下,序列化數據:res=myserial.BookSer(books,many=False)一條(一個book對象)  # books=models.Book.objects.first()


8.3、序列化方法SerializerMethodField  數據多對多查詢時使用,具體如下:

方式一:通過SerializerMethodField的自定義方法

from rest_framework import serializers
class BookSerial(serializers.Serializer):
nid=serializers.IntegerField()
# name這個名稱就是數據庫內的名稱name,這樣寫不安全。
# 如果要指定別名:把name改為name3時,需要指定source='name'對應到name3這個別名。
name3=serializers.CharField(source='name')
price=serializers.CharField()
publish_date=serializers.CharField()
publish = serializers.CharField()
# 調用類下面的某一個屬性的值,通過source='publish.email'來傳值。
# source的功能就是調用類下面某一個屬性或者方法(前提是這個屬性或者方法必須存在)。
# 如果類(A)下面還是一個類(B)時,調用類(B)下面的某個方法只需要source='類(B).方法名'
publish_email = serializers.CharField(source='publish.email')
# authors=serializers.CharField(source='authors.all')
# 方式一:拿到所有的authors的值
# SerializerMethodField可以自定義方法:方法的返回值就是authors的值
auth = serializers.SerializerMethodField()
def get_auth(self,obj):
# get_auth必須為(get_(auth))
# obj表示當前BookSerial對象。
authors=obj.authors.all() # 這裡拿到的是Book.authors的值,類似source='publish.email'拿到的值一樣
ll = [{'name':n.name,'age':n.age} for n in authors]
return ll # 這裡的返回值,就是請求拿到的值
# 通過視圖函數拿到的返回值
{
"status": 200,
"msg": "success",
"data": [
{
"nid": 1,
"name3": "xiyouji",
"price": "199.00",
"publish_date": "1999-09-09",
"publish": "北京出版社",
"publish_email": "[email protected]",
看這裡 "auth": [
{
"name": "sudada",
"age": 28
}
]
},
{
"nid": 2,
"name3": "sanguoyanyi",
"price": "299.00",
"publish_date": "1993-04-05",
"publish": "上海出版社",
"publish_email": "[email protected]",
看這裡 "auth": [
{
"name": "szq",
"age": 18
}
]
}
]
}

方式二:通過實例化自定義的AuthorSerial類,拿到Author數據表內的所有值

from rest_framework import serializers
class BookSerial(serializers.Serializer):
nid=serializers.IntegerField()
# name這個名稱就是數據庫內的名稱name,這樣寫不安全。
# 如果要指定別名:把name改為name3時,需要指定source='name'對應到name3這個別名。
name3=serializers.CharField(source='name')
price=serializers.CharField()
publish_date=serializers.CharField()
publish = serializers.CharField()
# 調用類下面的某一個屬性的值,通過source='publish.email'來傳值。
# source的功能就是調用類下面某一個屬性或者方法(前提是這個屬性或者方法必須存在)。
# 如果類(A)下面還是一個類(B)時,調用類(B)下面的某個方法只需要source='類(B).方法名'
publish_email = serializers.CharField(source='publish.email')
# authors=serializers.CharField(source='authors.all')
# 方式二:通過實例化自定義的AuthorSerial類,拿到Author數據表內的所有值
authors = serializers.SerializerMethodField()
def get_authors(self,obj):
authors=obj.authors.all()
ser=AuthorSerial(authors,many=True)
return ser.data
class AuthorSerial(serializers.Serializer):
nid=serializers.IntegerField()
name=serializers.CharField()
age=serializers.IntegerField()
author_telephone = serializers.CharField(source='author_detail.telephone')
author_birthday = serializers.CharField(source='author_detail.birthday')
# 通過視圖函數拿到的返回值
{
"status": 200,
"msg": "success",
"data": [
{
"nid": 1,
"name3": "xiyouji",
"price": "199.00",
"publish_date": "1999-09-09",
"publish": "北京出版社",
"publish_email": "[email protected]",
"authors": [ 看這裡,這裡拿到的值就是AuthorSerial的屬性。
{
"nid": 2,
"name": "sudada",
"age": 28,
"author_telephone": "222",
"author_birthday": "1999-09-09"
}
]
},
{
"nid": 2,
"name3": "sanguoyanyi",
"price": "299.00",
"publish_date": "1993-04-05",
"publish": "上海出版社",
"publish_email": "[email protected]",
"authors": [ 看這裡,這裡拿到的值就是AuthorSerial的屬性。
{
"nid": 1,
"name": "szq",
"age": 18,
"author_telephone": "111",
"author_birthday": "2000-06-11"
}
]
}
]
}

8.3.1、SerializerMethodField的用法總結:

   1.使用SerializerMethodField時,可以自定義方法A,方法A的返回值就是authors的值,方法如下:
   2.必須配套一個方法:方法的名字"get_authors"必須要等於get_(authors)
   3.def get_authors(self,obj):     # obj表示當前BookSer的所有對象。
       authors=obj.authors.all()
       ll = [{'name':author.name,'age':author.age} for author in authors]
       return ll
   4.在方法內可以繼續用序列化組件。

8.4、序列化組件ModelSerializer,用法一:查詢數據

# 序列化組件:ModelSerializer
from app01.models import Book
class BookSer(serializers.ModelSerializer):
class Meta:
# 指定要序列化的表模型,這裡以app01.models.Book為例
model=Book
# 把所有字段都序列化
fields="__all__"
publish = serializers.CharField()
authors = serializers.SerializerMethodField()
def get_authors(self,obj):
authors=obj.authors.all()
ser=AuthorSerial(authors,many=True)
return ser.data
"""
{
"status": 200,
"msg": "success",
"data": [
{
"nid": 1,
"publish": "北京出版社",
"authors": [
{
"nid": 2,
"name": "sudada",
"age": 28,
"author_telephone": "222",
"author_birthday": "1999-09-09"
}
],
"name": "xiyouji",
"price": "199.00",
"publish_date": "1999-09-09"
},
{
"nid": 2,
"publish": "上海出版社",
"authors": [
{
"nid": 1,
"name": "szq",
"age": 18,
"author_telephone": "111",
"author_birthday": "2000-06-11"
}
],
"name": "sanguoyanyi",
"price": "299.00",
"publish_date": "1993-04-05"
}
]
}
"""

序列化組件之ModelSerializer 的用法總結:

1.用法同Serializer一致,但也有不同點,如下:
class BookSer(serializers.ModelSerializer):
    class Meta:
    
    model=models.Book                             # 指定要序列化的表模型為Book
    fields = ['name','authors','publish']         # 表示序列化的表字段,'__all__'表示所有字段,['name','authors','publish']表示自定義字段
    exclude = ['nid']                             # 自定義[]列表內排除某些字段
    # 注意:fields和exclude不能同時使用!
    depth=2                                       # depth指定深度查詢

8.5、序列化組件的ModelSerializer,用法二:反序列化

 8.5.1、數據保存功能(通過post請求傳入數據,然後保存)

POST傳入數據:

 代碼如下:

# 在views.py文件中
from rest_framework.views import APIView
from rest_framework import serializers
from app01 import myserial,models
class Book(APIView):
def post(self,request):
print(request.data)
res=myserial.BookSer(data=request.data) # 通過request.data拿到數據,然後執行反序列化
if res.is_valid():
res.save() # 執行反序列方法,保存數據到DB裡面
else:
print(res.errors) # 打印錯誤日志
# {'authors': [ErrorDetail(string='This field is required.', code='required')]}
return HttpResponse('post')
# 在myserial.py文件中
from app01 import models
from rest_framework import serializers
# ModelSerializer用法
class BookSer(serializers.ModelSerializer):
class Meta:
# model:指定要序列化的表模型是Book
model=models.Book
# exclude:自定義[]列表內排除某些字段
exclude = ['authors']

  8.5.2、請求數據校驗,局部校驗與全局校驗

# urls.py文件中
urlpatterns = [
path('admin/', admin.site.urls),
# as_view()拿到的是CBTtest類裡面某個函數的名稱[詳細信息查看View類裡面的as_view函數,最終返回一個get|post函數的內存地址]以及一個request對象。
path('books/', views.Book.as_view()),
]
# 在views.py文件中
from rest_framework.views import APIView
from rest_framework import serializers
from app01 import models,myserial
class Book(APIView):
def post(self,request):
print(request.data)
res=myserial.BookSer(data=request.data) # 通過request.data拿到數據,然後執行反序列化
if res.is_valid():
res.save() # 執行反序列方法,保存數據到DB裡面
return HttpResponse("post_ok")
else:
print(res.errors) # 打印錯誤日志
# {'authors': [ErrorDetail(string='This field is required.', code='required')]}
return JsonResponse(res.errors,safe=False)
# 在myserial.py文件中
from app01 import models
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
# ModelSerializer用法
class BookSer(serializers.ModelSerializer):
class Meta:
model=models.Book
exclude = ['authors']
# 局部校驗
name=serializers.CharField()
def validate_name(self,value): # value就是前端傳過來的JSON值(只匹配數據表內"name"這個關鍵字的值)
if value.startswith('sb'): # 判斷數據表內"name"這個關鍵字的值是否以"sb"開頭
raise ValidationError('name字段不能以sb開頭') # {'name': [ErrorDetail(string='不能以sb開頭', code='invalid')]}
else:
return value
# 全局校驗
def validate(self,value):
# print(value) # value就是前端傳過來的JSON值:{'name': '西游記', 'price': '199', 'publish_date': '2016-12-19', 'publish': 1}
name=value.get('name')
if name.startswith('sb'):
raise ValidationError('name字段不能以sb開頭')
else:
return value
# 輸出如下:
{'non_field_errors': [ErrorDetail(string='name字段不能以sb開頭', code='invalid')]}

 8.5.3、序列化組件ModelSerializer的數據校驗和反序列化總結:

1.只有ModelSerializer有"res.save()"反序列化保存數據的方法。
2.用法
def post(self,request):
res=myserial.BookSer(data=request.data) # 生成一個序列化對象,通過request.data拿到數據,然後執行反序列化
if res.is_valid(): # 判斷字段是否校驗通過。
res.save() # 字段校驗通過,則執行反序列方法,保存數據到DB裡面
else:
print(res.errors) # 否則打印錯誤日志
# {'authors': [ErrorDetail(string='This field is required.', code='required')]}
return HttpResponse('post')

九、DRF視圖類的用法

9.1、視圖類的基本寫法(增、刪、改、查)

  方式一:最原始版本,增刪改查,需要手寫POST,GET等

# urls.py
urlpatterns = [
path('admin/', admin.site.urls),
# as_view()拿到的是CBTtest類裡面某個函數的名稱[詳細信息查看View類裡面的as_view函數,最終返回一個get|post函數的內存地址]以及一個request對象。
re_path('^books/$', views.Book.as_view()),
re_path('^books/(\d+)/', views.BookDetail.as_view()),
re_path('^books/(?P<id>\d+)/', views.BookDetail.as_view()),
]
# views.py
from django.shortcuts import render,HttpResponse,redirect
from django.http import JsonResponse
from django.views import View
# Create your views here.
from rest_framework.views import APIView
from app01 import models
from rest_framework import serializers
from app01 import myserial
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()
book=models.Book.objects.filter(pk=id).first() # 查詢單個數據
res=myserial.BookSer(instance=book,many=False) # 序列化查詢單個數據
response.msg='查詢成功'
response.data=res.data
return JsonResponse(response.get_dic,safe=False)
# 修改單個數據
def put(self,request,id):
response = MyResponse()
book = models.Book.objects.filter(pk=id).first()
# instance=book:表示被修改的對象
# data=request.data:表示修改的值
res = myserial.BookSer(instance=book, data=request.data)
if res.is_valid():
res.save() # 可以新增可以修改數據
# print(res.instance) # res.instance為一個對象<class 'app01.models.Book'>,拿到的值是字段"name"("三國演義")
print(res.data) # res.data拿到的就是前端的請求值: {'name': '三國演義', 'price': '188.00', 'publish_date': '2018-11-06', 'publish': 1}
response.msg = '修改成功'
response.data=res.data
return JsonResponse(response.get_dic,safe=False)
"""
{
"status": 100,
"msg": "修改成功",
"data": {
"name": "三國演義",
"price": "188.00",
"publish_date": "2018-11-06",
"publish": 1
}
}
"""
else:
response.msg = '修改失敗'
response.status=1001
response.data=res.errors
"""
{
"status": 1001,
"msg": "修改失敗",
"data": {
"name": [
"This field is required."
],
"price": [
"This field is required."
]
}
}
"""
return JsonResponse(response.get_dic,safe=False)
# 刪除單個數據
def delete(self,request,id):
response = MyResponse()
models.Book.objects.filter(pk=id).delete()
response.msg = '刪除成功'
return JsonResponse(response.get_dic,safe=False)
class Book(APIView):
# 查詢所有數據
def get(self,request):
response=MyResponse()
books=models.Book.objects.all()
res=myserial.BookSer(books,many=True) # many=True表示序列化多條數據
response.msg='查詢成功'
response.data=res.data
return JsonResponse(response.get_dic,safe=False)
# 新增一個數據
def post(self,request):
response = MyResponse()
res=myserial.BookSer(data=request.data) # 通過request.data拿到數據,然後執行反序列化
if res.is_valid():
res.save() # 執行反序列方法,保存數據到DB裡面
response.msg="新增數據成功"
response.data=res.data
else:
response.msg = '新增數據失敗'
response.status=1001
response.data=res.errors
return JsonResponse(response.get_dic,safe=False)
# myserial.py
from app01 import models
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
class BookSer(serializers.ModelSerializer):
class Meta:
model=models.Book
# fields='__all__'
fields=['name','price','publish_date','publish']
# 局部校驗,當修改數據表是,某個字段未填值,會報錯"該字段必填",給一個提示。
name=serializers.CharField(error_messages={"required":"該字段必填"})
price=serializers.CharField(error_messages={"required":"該字段必填"})
publish_date=serializers.CharField(error_messages={"required":"該字段必填"})
publish=serializers.CharField(error_messages={"required":"該字段必填"})

  方式二:需要手寫get,post等方法

# urls.py文件
urlpatterns = [
path('admin/', admin.site.urls),
re_path('^books/$', views.BookView.as_view()),
re_path('^books/(?P<pk>\d+)/', views.BookDetailView.as_view()),
]
# views.py文件
from django.shortcuts import render,HttpResponse,redirect
from django.http import JsonResponse
from app01 import models
from app01 import myserial
from rest_framework.views import APIView
from rest_framework.generics import GenericAPIView
from rest_framework.mixins import CreateModelMixin,RetrieveModelMixin,ListModelMixin,UpdateModelMixin,DestroyModelMixin
class Book(GenericAPIView,ListModelMixin,CreateModelMixin):
queryset=models.Book.objects.all()
serializer_class=myserial.BookSer
def get(self,request,*args,**kwargs):
return self.list(self,request,*args,**kwargs)
def post(self,request,*args,**kwargs):
return self.create(request,*args,**kwargs)
class BookDetail(GenericAPIView,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin):
queryset = models.Book
serializer_class = myserial.BookSer
def get(self,request,*args,**kwargs):
return self.retrieve(request,*args,**kwargs)
def put(self,request,*args,**kwargs):
return self.update(request,*args,**kwargs)
def delete(self,request,*args,**kwargs):
return self.destroy(request,*args,**kwargs)
# myserial.py
from app01 import models
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
class BookSer(serializers.ModelSerializer):
class Meta:
model=models.Book
# fields='__all__'
fields=['name','price','publish_date','publish']

 方式三:不需要手寫get,post等方法

# urls.py文件
urlpatterns = [
path('admin/', admin.site.urls),
re_path('^books/$', views.BookView.as_view()),
re_path('^books/(?P<pk>\d+)/', views.BookDetailView.as_view()),
]
# views.py
from rest_framework.generics import ListCreateAPIView,RetrieveUpdateDestroyAPIView
class BookView(ListCreateAPIView):
queryset=models.Book.objects.all()
serializer_class=myserial.BookSer
class BookDetailView(RetrieveUpdateDestroyAPIView):
queryset=models.Book.objects.all()
serializer_class=myserial.BookSer
# myserial.py
from app01 import models
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
class BookSer(serializers.ModelSerializer):
class Meta:
model=models.Book
# fields='__all__'
fields=['name','price','publish_date','publish']

  方式四:方法三的升級版,在urls.py處做了POST,GET請求方式的處理

# urls.py文件
urlpatterns = [
path('admin/', admin.site.urls),
re_path('^books/$', views.BookView.as_view({"get":"list","post":"create"})),
re_path('^books/(?P<pk>\d+)/', views.BookView.as_view({"get":"retrieve","put":"update","delete":"destroy"})),
]
# views.py
from rest_framework.viewsets import ModelViewSet
class BookView(ModelViewSet):
queryset=models.Book.objects.all()
serializer_class=myserial.BookSer
# myserial.py
from app01 import models
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
class BookSer(serializers.ModelSerializer):
class Meta:
model=models.Book
# fields='__all__'
fields=['name','price','publish_date','publish']

9.2、視圖類的基本寫法(增、刪、改、查),最終版本 -- 方式五

urls.py文件
urlpatterns = [
path('admin/', admin.site.urls),
# as_view()拿到的是CBTtest類裡面某個函數的名稱[詳細信息查看View類裡面的as_view函數,最終返回一個get|post函數的內存地址]以及一個request對象。
re_path('^books/$', views.BookView.as_view({'get':'get_all'})), # get請求響應get_all函數
re_path('^books/(?P<pk>\d+)/', views.BookView.as_view({'get':'get_one'})), # get請求響應get_one函數
]
views.py文件
from rest_framework.views import APIView
from rest_framework.viewsets import ViewSetMixin
class MyResponse():
def __init__(self):
self.status=100
self.msg=None
@property
def get_dic(self):
return self.__dict__ # 將類裡面的數據轉換成為字典
# ViewSetMixin 這個類可以重改路由,可以讓一個視圖函數可以響應多個(post|gey|put|delete)方法
class BookView(ViewSetMixin,APIView):
def get_all(self,request):
response = MyResponse()
book = models.Book.objects.all() # 查詢所有數據
res = myserial.BookSer(instance=book, many=True) # # 序列化查詢所有數據
response.msg = '查詢成功'
response.data = res.data
return JsonResponse(response.get_dic,safe=False)
def get_one(self,request,pk):
response=MyResponse()
book=models.Book.objects.filter(pk=pk).first() # 查詢單個數據
res=myserial.BookSer(instance=book,many=False) # 序列化查詢單個數據
response.msg='查詢成功'
response.data=res.data
return JsonResponse(response.get_dic,safe=False)
# myserial.py文件
from app01 import models
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
class BookSer(serializers.ModelSerializer):
class Meta:
model=models.Book
# fields='__all__'
fields=['name','price','publish_date','publish']
class AuthorSer(serializers.ModelSerializer):
class Meta:
model=models.Author
fields=['nid','age','name']

十、認證組件

      作用:校驗是否登錄

# urls.py文件
urlpatterns = [
path('admin/', admin.site.urls),
# re_path('^books/$', views.Book.as_view()),
re_path('^login/$', views.Login.as_view()),
re_path('^books/$', views.Book.as_view()),
]
views.py文件
from django.shortcuts import render,HttpResponse,redirect
from django.http import JsonResponse
from app01 import models
from app01 import myserial
from rest_framework.views import APIView
from rest_framework.viewsets import ViewSetMixin
# 登錄組件:登錄之後,返回前端一個字符串,那麼下次訪問時攜帶這個字符串,通過校驗字符串來判斷登錄是否有效
import hashlib
import time
# 生成隨機字符串
def get_token(user_name):
md=hashlib.md5()
md.update(user_name.encode('utf-8'))
md.update(str(time.time()).encode('utf-8'))
return md.hexdigest()
class MyResponse():
def __init__(self):
self.status=100
self.msg=None
@property
def get_dic(self):
return self.__dict__ # 將類裡面的數據轉換成為字典
# 用戶登錄認證方法
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
class myAuthen(BaseAuthentication):
def authenticate(self,request):
token=request.query_params.get('token')
res=models.UserToken.objects.filter(token=token).first()
if res:
# 要寫多個類,這裡返回None
# return None
return res.user,res # 最後一個認證類,返回2個對象
else:
raise AuthenticationFailed('您未登錄,登錄失敗')
# 用戶登錄的方法
class Login(APIView):
authentication_classes = [] # 用戶登錄認證,設置為空時,表示不需要認證
def post(self,request):
response=MyResponse()
name=request.data.get('name')
pwd=request.data.get('pwd')
user = models.User.objects.filter(name=name,pwd=pwd).first()
if user:
response.msg = '登錄成功'
# 登錄成功後生成一個隨機字符串,以後再發請求,都攜帶這個字符串
token = get_token(name)
response.token=token
# 把隨機字符串保存到數據庫
models.UserToken.objects.update_or_create(user=user,defaults={'token':token}) # update_or_create表示:有就更新,沒有就創建
else:
response.msg = '用戶名或密碼錯誤'
response.status = 101
return JsonResponse(response.get_dic,safe=False)
# 用戶登錄成功後,才可以查看
class Book(APIView):
authentication_classes = [myAuthen,] # authentication_classes會調用MyAuthen類,實例化出authenticate方法
def get(self,request):
# print(request.user) # User object (1) 用戶對象,user為源碼固定寫法
# print(request.auth) # UserToken object (1) Token對象,auth為源碼固定寫法
response = MyResponse()
# 登錄後才能訪問
book = models.Book.objects.all()
res = myserial.BookSer(instance=book,many=True)
response.msg = '查詢成功'
response.data = res.data
return JsonResponse(response.get_dic,safe=False)
# models.py文件
from django.db import models
class User(models.Model):
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
pwd = models.CharField(max_length=32,null=True)
# 用戶登錄後,存儲一個隨機字符串,下次用戶在發起請求時,會攜帶這個隨機字符串,
class UserToken(models.Model):
user=models.OneToOneField(to=User,to_field='nid',unique=True,on_delete=models.CASCADE)
token=models.CharField(max_length=64)
# 請求驗證的方式:
post登錄請求:http://127.0.0.1:8000/login/ # 輸入用戶名和密碼(JSON格式)
{
"name":"szq",
"pwd":"123"
}
get登錄成功後查看數據請求:http://127.0.0.1:8000/books/?token=9da5d186b82756855a2a9dfe00f00f7d

 認證組件總結:

1、作用:校驗是否登錄
2、首先定義一個類,繼承BaseAuthentication,寫一個方法,authenticate,在方法內部寫認證過程。
認證通過,返貨None,或者2個對象(auth,user),這2個對象在視圖類的request中可以取出來。
class myAuthen(BaseAuthentication):
def authenticate(self,request):
token=request.query_params.get('token')
res=models.UserToken.objects.filter(token=token).first()
if res:
# 要寫多個類,這裡返回None
# return None
return res.user,res # 最後一個認證類,返回2個對象
else:
raise AuthenticationFailed('您未登錄,登錄失敗')
3、局部使用:在需要驗證的類組件下,加上:authentication_classes = [myAuthen,] 也就是說,想要訪問Book類必須通過登錄驗證 "authentication_classes = [myAuthen,]"
4、全局使用:在settings.py文件中加上:"DEFAULT_PERMISSION_CLASSES",如下:
REST_FRAMEWORK = {
'DEFAULT_PARSER_CLASSES':[
'rest_framework.parsers.JSONParser'
],
"DEFAULT_AUTHENTICATION_CLASSES": ["app01.auth.myAuthen", ]
}

 認證組件補充 - 不存數據庫的token驗證:

def get_token(id,salt='123'):
import hashlib
md=hashlib.md5()
md.update(bytes(str(id),encoding='utf-8'))
md.update(bytes(salt,encoding='utf-8'))
return md.hexdigest()+'|'+str(id)
def check_token(token,salt='123'):
ll=token.split('|')
import hashlib
md=hashlib.md5()
md.update(bytes(ll[-1],encoding='utf-8'))
md.update(bytes(salt,encoding='utf-8'))
if ll[0]==md.hexdigest():
return True
else:
return False
class TokenAuth():
def authenticate(self, request):
token = request.GET.get('token')
success=check_token(token)
if success:
return
else:
raise AuthenticationFailed('認證失敗')
def authenticate_header(self,request):
pass
class Login(APIView):
def post(self,reuquest):
back_msg={'status':1001,'msg':None}
try:
name=reuquest.data.get('name')
pwd=reuquest.data.get('pwd')
user=models.User.objects.filter(username=name,password=pwd).first()
if user:
token=get_token(user.pk)
# models.UserToken.objects.update_or_create(user=user,defaults={'token':token})
back_msg['status']='1000'
back_msg['msg']='登錄成功'
back_msg['token']=token
else:
back_msg['msg'] = '用戶名或密碼錯誤'
except Exception as e:
back_msg['msg']=str(e)
return Response(back_msg)
from rest_framework.authentication import BaseAuthentication
class TokenAuth():
def authenticate(self, request):
token = request.GET.get('token')
token_obj = models.UserToken.objects.filter(token=token).first()
if token_obj:
return
else:
raise AuthenticationFailed('認證失敗')
def authenticate_header(self,request):
pass
class Course(APIView):
authentication_classes = [TokenAuth, ]
def get(self, request):
return HttpResponse('get')
def post(self, request):
return HttpResponse('post')

十一、權限組件 --  和認證組件寫法基本相同

      作用:校驗已經登錄的用戶是否有權限訪問

urls.py文件
urlpatterns = [
path('admin/', admin.site.urls),
# re_path('^books/$', views.Book.as_view()),
re_path('^login/$', views.Login.as_view()),
re_path('^books/$', views.Book.as_view()),
]
views.py文件
from django.shortcuts import render,HttpResponse,redirect
from django.http import JsonResponse
from app01 import models
from app01 import myserial
from rest_framework.views import APIView
from rest_framework.viewsets import ViewSetMixin
# 登錄組件:登錄之後,返回前端一個字符串,那麼下次訪問時攜帶這個字符串,通過校驗字符串來判斷登錄是否有效
import hashlib
import time
# 生成隨機字符串
def get_token(user_name):
md=hashlib.md5()
md.update(user_name.encode('utf-8'))
md.update(str(time.time()).encode('utf-8'))
return md.hexdigest()
class MyResponse():
def __init__(self):
self.status=100
self.msg=None
@property
def get_dic(self):
return self.__dict__ # 將類裡面的數據轉換成為字典
# 用戶登錄認證方法
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from rest_framework.permissions import BasePermission
class myAuthen(BaseAuthentication): # 認證組件
def authenticate(self,request):
token=request.query_params.get('token')
res=models.UserToken.objects.filter(token=token).first()
if res:
# 要寫多個類,這裡返回None
# return None
return res.user,res # 最後一個認證類,返回2個對象
else:
raise AuthenticationFailed('您未登錄,登錄失敗')
class myPermission(BasePermission): # 權限認證
message = '不是超級用戶,無權訪問!'
def has_permission(self,request,view):
if request.user.usertype != 3: # 判斷用戶的權限類型(權限值)
return False
else:
return True
# 用戶登錄的方法
class Login(APIView):
authentication_classes = [] # 用戶登錄認證,設置為空時,表示不需要認證
def post(self,request):
response=MyResponse()
name=request.data.get('name')
pwd=request.data.get('pwd')
user = models.User.objects.filter(name=name,pwd=pwd).first()
if user:
response.msg = '登錄成功'
# 登錄成功後生成一個隨機字符串,以後再發請求,都攜帶這個字符串
token = get_token(name)
response.token=token
# 把隨機字符串保存到數據庫
models.UserToken.objects.update_or_create(user=user,defaults={'token':token}) # update_or_create表示:有就更新,沒有就創建
else:
response.msg = '用戶名或密碼錯誤'
response.status = 101
return JsonResponse(response.get_dic,safe=False)
# 用戶登錄成功後,才可以查看
class Book(APIView):
authentication_classes = [myAuthen,] # authentication_classes會調用MyAuthen類,實例化出authenticate方法
permission_classes = [myPermission,] # permission_classes會調用myPermission類,實例化出has_permission方法
def get(self,request):
# print(request.user) # User object (1) 用戶對象,user為源碼固定寫法
# print(request.auth) # UserToken object (1) Token對象,auth為源碼固定寫法
response = MyResponse()
# 登錄後才能訪問
book = models.Book.objects.all()
res = myserial.BookSer(instance=book,many=True)
response.msg = '查詢成功'
response.data = res.data
return JsonResponse(response.get_dic,safe=False)
# models.py文件
from django.db import models
class User(models.Model):
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
pwd = models.CharField(max_length=32,null=True)
mychoice=((1,'普通用戶'),(2,'超級用戶'),(3,'宇宙用戶'))
usertype=models.IntegerField(choices=mychoice,default=1)
# 指定usertype的值,只能從mychoice裡面取。
# 用戶登錄後,存儲一個隨機字符串,下次用戶在發起請求時,會攜帶這個隨機字符串,
class UserToken(models.Model):
user=models.OneToOneField(to=User,to_field='nid',unique=True,on_delete=models.CASCADE)
token=models.CharField(max_length=64)
# 請求驗證的方式:
post登錄請求:http://127.0.0.1:8000/login/ # 輸入用戶名和密碼(JSON格式)
{
"name":"szq",
"pwd":"123"
}
get登錄成功後查看數據請求:http://127.0.0.1:8000/books/?token=9da5d186b82756855a2a9dfe00f00f7d

  權限組件使用總結:

1、作用:校驗用戶是否有權限訪問:
2、因為是在"perform_authentication"用戶認證之後才執行,所以可以取出user(認證處需要return user,否則就沒有)
class myPermission():
message = '不是超級用戶,無權訪問!'
def has_permission(self,request,view):
if request.user.usertype != 3:
return False
else:
return True
3、局部使用:在需要驗證的類組件下,加上:permission_classes = [myPermission,]
4、全局使用:在settings.py文件中加上:"DEFAULT_PERMISSION_CLASSES",如下:
REST_FRAMEWORK = {
'DEFAULT_PARSER_CLASSES':[
'rest_framework.parsers.JSONParser'
],
"DEFAULT_AUTHENTICATION_CLASSES": ["app01.auth.myAuthen", ],
"DEFAULT_PERMISSION_CLASSES": ["app01.auth.myPermission", ]
}

十二、頻率組件

      作用:限制已登錄用戶的頁面訪問次數(每分鐘允許訪問幾次)

urls.py文件
urlpatterns = [
path('admin/', admin.site.urls),
# re_path('^books/$', views.Book.as_view()),
re_path('^login/$', views.Login.as_view()),
re_path('^books/$', views.Book.as_view()),
]
views.py文件
from django.shortcuts import render,HttpResponse,redirect
from django.http import JsonResponse
from app01 import models
from app01 import myserial
from rest_framework.views import APIView
from rest_framework.viewsets import ViewSetMixin
# 登錄組件:登錄之後,返回前端一個字符串,那麼下次訪問時攜帶這個字符串,通過校驗字符串來判斷登錄是否有效
import hashlib
import time
# 生成隨機字符串
def get_token(user_name):
md=hashlib.md5()
md.update(user_name.encode('utf-8'))
md.update(str(time.time()).encode('utf-8'))
return md.hexdigest()
class MyResponse():
def __init__(self):
self.status=100
self.msg=None
@property
def get_dic(self):
return self.__dict__ # 將類裡面的數據轉換成為字典
# 用戶登錄認證方法
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from rest_framework.permissions import BasePermission
# 認證組件
class myAuthen(BaseAuthentication):
def authenticate(self,request):
token=request.query_params.get('token')
res=models.UserToken.objects.filter(token=token).first()
if res:
# 要寫多個類,這裡返回None
# return None
return res.user,res # 最後一個認證類,返回2個對象
else:
raise AuthenticationFailed('您未登錄,登錄失敗')
# 權限認證
class myPermission(BasePermission):
message = '不是超級用戶,無權訪問!'
def has_permission(self,request,view):
if request.user.usertype != 3: # 判斷用戶的權限類型(權限值)
return False
else:
return True
# 頻率組件認證 -- restful框架自帶的功能:需要重寫2個方法:
# 1、scope = "test_szq" # "test_szq" 是setting.py文件中 "REST_FRAMEWORK" 全局認證裡面定義的
# 2、重寫 get_cache_key 方法,如下:
from rest_framework.throttling import SimpleRateThrottle
class MyThrottles(SimpleRateThrottle):
scope = "test_szq"
def get_cache_key(self, request, view):
return self.get_ident(request) # 方法一
# return request.META.get("REMOTE_ADDR") # 方法二
# 用戶登錄的方法
class Login(APIView):
authentication_classes = [] # 用戶登錄認證,設置為空時,表示不需要認證
def post(self,request):
response=MyResponse()
name=request.data.get('name')
pwd=request.data.get('pwd')
user = models.User.objects.filter(name=name,pwd=pwd).first()
if user:
response.msg = '登錄成功'
# 登錄成功後生成一個隨機字符串,以後再發請求,都攜帶這個字符串
token = get_token(name)
response.token=token
# 把隨機字符串保存到數據庫
models.UserToken.objects.update_or_create(user=user,defaults={'token':token}) # update_or_create表示:有就更新,沒有就創建
else:
response.msg = '用戶名或密碼錯誤'
response.status = 101
return JsonResponse(response.get_dic,safe=False)
# 用戶登錄成功後,才可以查看
class Book(APIView):
authentication_classes = [myAuthen,] # authentication_classes會調用MyAuthen類,實例化出authenticate方法
permission_classes = [myPermission,] # permission_classes會調用myPermission類,實例化出has_permission方法
def get(self,request):
# print(request.user) # User object (1) 用戶對象,user為源碼固定寫法
# print(request.auth) # UserToken object (1) Token對象,auth為源碼固定寫法
response = MyResponse()
# 登錄後才能訪問
book = models.Book.objects.all()
res = myserial.BookSer(instance=book,many=True)
response.msg = '查詢成功'
response.data = res.data
return JsonResponse(response.get_dic,safe=False)
# models.py文件
from django.db import models
class User(models.Model):
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
pwd = models.CharField(max_length=32,null=True)
mychoice=((1,'普通用戶'),(2,'超級用戶'),(3,'宇宙用戶'))
usertype=models.IntegerField(choices=mychoice,default=1)
# 指定usertype的值,只能從mychoice裡面取。
# 用戶登錄後,存儲一個隨機字符串,下次用戶在發起請求時,會攜帶這個隨機字符串,
class UserToken(models.Model):
user=models.OneToOneField(to=User,to_field='nid',unique=True,on_delete=models.CASCADE)
token=models.CharField(max_length=64)
# 請求驗證的方式:
post登錄請求:http://127.0.0.1:8000/login/ # 輸入用戶名和密碼(JSON格式)
{
"name":"szq",
"pwd":"123"
}
get登錄成功後查看數據請求:http://127.0.0.1:8000/books/?token=9da5d186b82756855a2a9dfe00f00f7d
訪問次數超過限制後報錯: "detail": "Request was throttled. Expected available in 58 seconds."

  頻率組件使用總結:

1、作用:校驗已登錄用戶對某個頁面的訪問頻率(每分鐘可以訪問多少次)
2、頻率組件認證 -- restful框架自帶的功能:需要重寫2個方法:
# 1、scope = "test_szq" # "test_szq" 是setting.py文件中 "REST_FRAMEWORK" 全局認證裡面定義的
# 2、重寫 get_cache_key 方法,如下:
from rest_framework.throttling import SimpleRateThrottle
class MyThrottles(SimpleRateThrottle):
scope = "test_szq"
def get_cache_key(self, request, view):
return self.get_ident(request) # 方法一
# return request.META.get("REMOTE_ADDR") # 方法二
3、局部使用:在需要驗證的類組件下,加上:permission_classes = [MyThrottles,]
4、全局使用:在settings.py文件中加上:"DEFAULT_PERMISSION_CLASSES",如下:
REST_FRAMEWORK = {
'DEFAULT_PARSER_CLASSES':[
'rest_framework.parsers.JSONParser'
],
"DEFAULT_AUTHENTICATION_CLASSES": ["app01.auth.myAuthen", ],
"DEFAULT_PERMISSION_CLASSES": ["app01.auth.myPermission", ],
'DEFAULT_THROTTLE_RATES': {
'test_szq': '5/m' # 一分鐘訪問三次
}
}
訪問次數超過限制後報錯: "detail": "Request was throttled. Expected available in 58 seconds."

十三、分頁器

作用:把數據庫查詢到的數據按照指定的行數顯示,比如一頁只顯示5行數據,或者10行數據

13.1、簡單分頁例子:

class Books(APIView):
# 查詢數據的接口
def get(self,request):
response=my_response.MyResponse()
books=Book.objects.all()
# 分頁器
'''
page_size = api_settings.PAGE_SIZE 每頁大小(顯示多少數據)
page_query_param = 'page2' 查詢條件:"?page=2","2"表示第二頁,和page_size關聯使用
page_size_query_param = None 指定每頁的大小
max_page_size = None 每頁的大小,最多顯示多少條,和page_size_query_param關聯使用
'''
page=PageNumberPagination()
page.page_size=5
page.page_query_param='page'
page.page_size_query_param='max'
page.max_page_size=3
page_book=page.paginate_queryset(books, request, view=self)
ser=my_serialzers.BookSerial(page_book,many=True)
response.data=ser.data
return JsonResponse(response.get_dic,safe=False)
請求接口:http://127.0.0.1:8000/app01/index/?page=1&max=100
返回數據:
{
"status": 200,
"msg": null,
"data": [
{
"nid": 1,
"name": "xiyouji",
"price": "999.00",
"publish_date": "1999-09-09",
"publish": "北京出版社",
"publish_email": "[email protected]",
"authors": [
{
"nid": 2,
"name": "sudada",
"age": 28,
"author_telephone": "222",
"author_birthday": "1999-09-09"
}
]
},
{
"nid": 2,
"name": "sanguoyanyi",
"price": "299.00",
"publish_date": "1993-04-05",
"publish": "上海出版社",
"publish_email": "[email protected]",
"authors": [
{
"nid": 1,
"name": "szq",
"age": 18,
"author_telephone": "111",
"author_birthday": "2000-06-11"
}
]
},
{
"nid": 3,
"name": "人間世",
"price": "399.00",
"publish_date": "2021-12-01",
"publish": "北京出版社",
"publish_email": "[email protected]",
"authors": []
}
]
}

13.2、偏移分頁例子:

class Books(APIView):
# 查詢數據的接口
def get(self,request):
response=my_response.MyResponse()
books=Book.objects.all()
# 分頁器
'''
default_limit = api_settings.PAGE_SIZE 默認每頁顯示的數據條數
limit_query_param = 'limit' 查詢的條數,limit=10就查詢10條,但優先級在default_limit之後
offset_query_param = 'offset' 偏移條數
max_limit = None 最大顯示數
'''
page=LimitOffsetPagination()
page.default_limit=2
page.limit_query_param="limit"
page.limit_query_param="offset"
page.limit_query_param=5
page_book=page.paginate_queryset(books, request, view=self)
ser=my_serialzers.BookSerial(page_book,many=True)
response.data=ser.data
return JsonResponse(response.get_dic,safe=False)

13.3、加密分頁例子:

 想查看下一頁的數據,需要請求返回數據裡面的"next"字段對應的地址(默認是加密的)

 只能向下查看,不能向上。

class Books(APIView):
# 查詢數據的接口
def get(self,request):
response=my_response.MyResponse()
books=Book.objects.all()
# 分頁器
'''
cursor_query_param = 'cursor' 查詢字段,這裡返回的是加密的,不能手動指定上/下頁
page_size = api_settings.PAGE_SIZE 默認每頁條數
ordering = '-created' 按什麼排序
'''
page=CursorPagination()
page.page_size=2
page.ordering="nid"
page_book=page.paginate_queryset(books, request, view=self)
ser=my_serialzers.BookSerial(page_book,many=True)
response.data=ser.data
return page.get_paginated_response(ser.data)
請求:http://127.0.0.1:8000/app01/index/?cursor=cD0y
響應:{
"next": "http://127.0.0.1:8000/app01/index/?cursor=cD00", # 想查看下一頁的數據,需要請求這個地址(默認是加密的)
"previous": "http://127.0.0.1:8000/app01/index/?cursor=cj0xJnA9Mw%3D%3D",
"results": [
{
"nid": 3,
"name": "人間世",
"price": "399.00",
"publish_date": "2021-12-01",
"publish": "北京出版社",
"publish_email": "[email protected]",
"authors": []
},
{
"nid": 4,
"name": "人間世1",
"price": "399.00",
"publish_date": "2021-12-01",
"publish": "北京出版社",
"publish_email": "[email protected]",
"authors": []
}
]
}

十四、響應器

  作用:顯示一個優雅的頁面

  顯示頁面主要使用的是:from rest_framework.response import Response 這個"Response"模塊,也就是說返回的值在浏覽器頁面想要好看,必須 "return Response(response.get_dic)"。

# urls.py文件中
urlpatterns = [
path('admin/', admin.site.urls),
# re_path('^books/$', views.Book.as_view()),
re_path('^books/$', views.Book.as_view()),
]
# views.py文件中
from rest_framework.response import Response
from rest_framework.renderers import HTMLFormRenderer,BrowsableAPIRenderer,JSONRenderer
class Book(APIView):
renderer_classes = [JSONRenderer,BrowsableAPIRenderer] # "JSONRenderer": 只返回純JSON格式的字符串(局部使用)
def get(self,request):
response = MyResponse()
book = models.Book.objects.all()
res = myserial.BookSer(instance=book,many=True)
response.msg = '查詢成功'
response.data = res.data
return Response(response.get_dic)

浏覽器查看的格式:

   響應器使用總結:

1、作用:顯示一個相對優雅的頁面
2、局部使用:在需要驗證的類組件下,加上:
renderer_classes = [JSONRenderer,BrowsableAPIRenderer]
3、全局使用:在settings.py文件中加上:"DEFAULT_PERMISSION_CLASSES",如下:
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES' : [
'rest_framework.renderers.JSONRenderer', # 響應json格式的數據
'rest_framework.renderers.BrowsableAPIRenderer', # 響應json格式的數據,浏覽器優雅顯示
] # 響應器的全局使用
}

十四、URL控制器(路由控制routers),自動注冊路由

# urls.py文件中
from django.contrib import admin
from django.urls import path,re_path
from app01 import views
from django.conf.urls import url,include
from rest_framework import routers
router = routers.DefaultRouter()
router.register('book', views.BooksDetail) # book:URL的名稱,views.BooksDetail:對應view視圖的BooksDetail類
urlpatterns = [
path('admin/', admin.site.urls),
# url控制器,手動版,需要手動定義傳參,並且在Book類下,使用"*args,**kwargs"接收。
# re_path('^books/$', views.Book.as_view({"get": "get_all"})),
# re_path('^books.(?P<format>\w+)', views.Book.as_view({"get": "get_all"})),
# url控制器,全自動版
re_path('^', include(router.urls)), # 這裡不需要在寫具體的URL名稱了,因為在router.register裡已經定義好了URL為book,對應的view視圖為Book類。
}
# views.py文件中
class BooksDetail(ModelViewSet):
queryset = Book.objects.all()
serializer_class = my_serialzers.BookSerial
# 響應數據的格式
renderer_classes = [JSONRenderer,BrowsableAPIRenderer]
# 查詢數據的接口
def get(self,request):
response=my_response.MyResponse()
response.msg="success"
books=Book.objects.all()
ser=my_serialzers.BookSerial(books,many=True)
response.data=ser.data
return Response(response.get_dic)
訪問請求:
普通請求:http://127.0.0.1:8000/book/?token=464ba5789d79fb655be3e58c9a0f40ea
JSON格式的請求:http://127.0.0.1:8000/book.json?token=464ba5789d79fb655be3e58c9a0f40ea
如果請求的URL格式不對,則拋出異常如下:
#########################################################################################
Using the URLconf defined in djangoresful_20190331_28.urls, Django tried these URL patterns, in this order:
admin/
^ ^book/$ [name='book-list']
^ ^book\.(?P<format>[a-z0-9]+)/?$ [name='book-list']
^ ^book/(?P<pk>[^/.]+)/$ [name='book-detail']
^ ^book/(?P<pk>[^/.]+)\.(?P<format>[a-z0-9]+)/?$ [name='book-detail']
^ ^$ [name='api-root']
^ ^\.(?P<format>[a-z0-9]+)/?$ [name='api-root']
^login/$
The current path, book/.json, didn't match any of these.

十五、版本控制

# 在urls.py文件中
urlpatterns = [
path('admin/', admin.site.urls),
# 以GET請求的方式請求時,使用這種方式
# re_path('^books/$', views.Book.as_view()),
# re_path('^login/$', views.Login.as_view()),
# 以URL請求的方式請求時,使用這種方式
re_path('^(?P<version>\w+)/books/', views.Book.as_view()),
re_path('^login/$', views.Login.as_view()),
]
# 在views.py文件中
from rest_framework.response import Response
from rest_framework.versioning import QueryParameterVersioning,AcceptHeaderVersioning,NamespaceVersioning,URLPathVersioning
class BookSer(serializers.ModelSerializer):
class Meta:
model=models.Book
# fields='__all__'
fields=['name','price','publish_date','publish']
class Book(APIView):
# versioning_class = QueryParameterVersioning # 以GET請求的方式請求時,使用"QueryParameterVersioning"
versioning_class = URLPathVersioning # 以URL請求的方式,使用"URLPathVersioning"
def get(self,request,*args,**kwargs):
response = MyResponse()
print(request.version)
book = models.Book.objects.all()
res = myserial.BookSer(instance=book,many=True)
response.msg = '查詢成功'
response.data = res.data
return Response(response.get_dic)
# modules.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=False)
publish = models.ForeignKey(to='Publish',to_field='nid',on_delete=models.CASCADE)
authors=models.ManyToManyField(to='Author')
def __str__(self):
return self.name
URL請求:http://127.0.0.1:8000/v2/books/ # 只允許v1和v2版本訪問

  版本控制使用總結:

1、作用:允許某個版本可以訪問,某個版本不能訪問(體現在URL的路徑上)
2、局部使用:在需要驗證的類組件下,加上:versioning_class = URLPathVersioning
3、全局使用:在settings.py文件中加上:"DEFAULT_PERMISSION_CLASSES",如下:
REST_FRAMEWORK = {
# 全局使用版本控制
'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.QueryParameterVersioning',
'DEFAULT_VERSION': 'v1', # 默認版本(從request對象裡取不到,顯示的默認值)
'ALLOWED_VERSIONS': ['v1', 'v2'], # 允許的版本
'VERSION_PARAM': 'version' # URL中獲取值的key
}


  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式師世界 All Rights Reserved