## 什么是restful
* REST與技術無關,代表的是一種軟件架構風格,REST是Representational State Transfer的簡稱,中文翻譯為“表征狀態轉移”
* REST從資源的角度類審視整個網絡,它將分布在網絡中某個節點的資源通過URL進行標識,客戶端應用通過URL來獲取資源的表征,獲得這些表征致使這些應用轉變狀態
* 所有的數據,不過是通過網絡獲取的還是操作(增刪改查)的數據,都是資源,將一切數據視為資源是REST區別與其他架構風格的最本質屬性
* 對于REST這種面向資源的架構風格,有人提出一種全新的結構理念,即:面向資源架構(ROA:Resource Oriented Architecture)
## 什么是API
1、什么是API?
答:API就是接口,提供的url。接口有兩個用途:
* \- 為別人提供服務
* \- 前后端分離,一個寫vue,一個寫后端,他們之間都是通過ajax請求
## restful API設計規范
* API與用戶的通信協議,總是使用HTTPS協議。
* 域名?
* https://api.example.com? ? ? ? ? ? ? ? ? ? ? ? ?盡量將API部署在專用域名(會存在跨域問題)
* https://example.org/api/? ? ? ? ? ? ? ? ? ? ? ? API很簡單
* 版本
* URL,如:https://api.example.com/v1/
* 請求頭? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 跨域時,引發發送多次請求
* 路徑,視網絡上任何東西都是資源,均使用名詞表示(可復數)
* https://api.example.com/v1/zoos
* https://api.example.com/v1/animals
* https://api.example.com/v1/employees
* method
* GET? ? ? :從服務器取出資源(一項或多項)
* POST? ? :在服務器新建一個資源
* PUT? ? ? :在服務器更新資源(客戶端提供改變后的完整資源)
* PATCH? :在服務器更新資源(客戶端提供改變的屬性)
* DELETE :從服務器刪除資源
* 過濾,通過在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:指定篩選條件
* 狀態碼
```
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:返回一個空文檔
~~~
* Hypermedia API,RESTful API最好做到Hypermedia,即返回結果中提供鏈接,連向其他API方法,使得用戶不查文檔,也知道下一步應該做什么。
## 基于Django實現restful api
參考博客:https://www.cnblogs.com/wusir66/p/10016584.html
## 基于Django Rest Framework框架實現
PS:以下介紹的使用方式都是一些常用的,還有一些方式使用較少,在此不做介紹。
### 一、自定義用戶認證
在models.py的UserInfo表中創建一些數據
```
from django.db import models
class UserInfo(models.Model):
user_type_choices = (
(1,'普通用戶'),
(2,'VIP'),
(3,'SVIP'),
)
user_type = models.IntegerField(choices=user_type_choices)
username = models.CharField(max_length=32,unique=True)
password = models.CharField(max_length=64)
class UserToken(models.Model):
user = models.OneToOneField(to='UserInfo')
token = models.CharField(max_length=64)
models.py
```
```
from django.http import JsonResponse
from rest_framework.views import APIView
from app.models import *
import hashlib
import time
#創建token字符串
def md5(user):
ctime = str(time.time())
m = hashlib.md5(bytes(user,encoding='utf-8'))
m.update(bytes(ctime,encoding='utf-8'))
return m.hexdigest()
#找到指定用戶給其token值
class AuthView(APIView):
authentication_classes = []
def post(self,request,*args,**kwargs):
ret = {'code':1000,'msg':None}
try:
name = request._request.POST.get('username')
pwd = request._request.POST.get('password')
obj = UserInfo.objects.filter(username=name,password=pwd).first()
if not obj:
ret['code'] = 1001
ret['msg'] = '用戶名或密碼錯誤'
token = md5(name)
UserToken.objects.update_or_create(user=obj,defaults={'token':token})
ret['token'] = token
except Exception as e:
ret['code'] = 1002
ret['msg'] = '請求異常'
return JsonResponse(ret)
#利用token值來進行認證
class OrderView(APIView):
def get(self,request,*args,**kwargs):
ret = {'code':1000,'msg':None,'data':None}
order = {'goods':'food'}
try:
ret['data'] = order
except Exception as e:
ret['msg'] = '有問題'
return JsonResponse(ret)
view.py
```
在app目錄下創建一個rest\_utils的文件夾
```
from rest_framework.authentication import BaseAuthentication
from app.models import *
from rest_framework import exceptions
class MyAuthentication(BaseAuthentication):
def authenticate(self, request):
token = request._request.GET.get('token')
obj = UserToken.objects.filter(token=token).first()
if not obj:
raise exceptions.AuthenticationFailed('用戶未認證')
return (obj.user,obj)
def authenticate_header(self, request):
pass
auth.py(具體認證代碼)
```
配置文件
~~~
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES':['app.rest_utils.auth.MyAuthentication',],
}
~~~
如果有某些類不需要使用認證,可以在類中加上以下代碼
~~~
authentication_classes = []
~~~
源碼大致流程
```
def dispatch(self, request, *args, **kwargs):
"""
`.dispatch()` is pretty much the same as Django's regular dispatch,
but with extra hooks for startup, finalize, and exception handling.
"""
self.args = args
self.kwargs = kwargs
第一步:對request進行加工(添加數據)
request = self.initialize_request(request, *args, **kwargs)
self.request = request
self.headers = self.default_response_headers # deprecate?
try:
#第二步:
#處理版權信息
#認證
#權限
#請求用戶進行訪問頻率的限制
self.initial(request, *args, **kwargs)
# Get the appropriate handler method
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(),
self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
# 第三步、執行:get/post/put/delete函數
response = handler(request, *args, **kwargs)
except Exception as exc:
response = self.handle_exception(exc)
#第四步、 對返回結果再次進行加工
self.response = self.finalize_response(request, response, *args, **kwargs)
return self.response
```
源碼具體流程
請求來了先走dispatch方法做分發
**1、對request進行加工(添加數據)**
**a、首先??request = self.initialize\_request(request, \*args, \*\*kwargs)點進去,會發現:在Request里面多加了四個,如下**
```
def initialize_request(self, request, *args, **kwargs):
"""
Returns the initial request object.
"""
#把請求弄成一個字典返回
parser_context = self.get_parser_context(request)
return Request(
request,
parsers=self.get_parsers(), #解析數據,默認的有三種方式,可點進去看
#self.get_authenticator優先找自己的,沒有就找父類的
authenticators=self.get_authenticators(), #獲取認證相關的所有類并實例化,傳入request對象供Request使用
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
```
**b、獲取認證相關的類的具體? ?authenticators=self.get\_authenticators()**
~~~
def get_authenticators(self):
"""
Instantiates and returns the list of authenticators that this view can use.
"""
#返回的是對象列表
return [auth() for auth in self.authentication_classes] #[SessionAuthentication,BaseAuthentication]
~~~
**c、查看認證的類:self.authentication\_classes**
~~~
authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES #默認的,如果自己有會優先執行自己的
~~~
**d、接著走進api\_settings**
```
api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS) #點擊繼承的DEFAULTS類
DEFAULTS類
DEFAULTS = {
# Base API policies
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.SessionAuthentication', #這時候就找到了他默認認證的類了,可以導入看看
'rest_framework.authentication.BasicAuthentication'
),
```
**e、導入了類看看類里面具體干了什么**
~~~
from rest_framework.authentication import SessionAuthentication
from rest_framework.authentication import BaseAuthentication
~~~
**f、看到里面有個authenticate方法和authenticate\_header方法**
```
class BaseAuthentication(object):
"""
All authentication classes should extend BaseAuthentication.
"""
def authenticate(self, request):
"""
Authenticate the request and return a two-tuple of (user, token).
"""
raise NotImplementedError(".authenticate() must be overridden.")
def authenticate_header(self, request):
"""
Return a string to be used as the value of the `WWW-Authenticate`
header in a `401 Unauthenticated` response, or `None` if the
authentication scheme should return `403 Permission Denied` responses.
"""
pass
```
**具體處理認證,從headers里面能獲取用戶名和密碼**
```
class BasicAuthentication(BaseAuthentication):
"""
HTTP Basic authentication against username/password.
"""
www_authenticate_realm = 'api'
def authenticate(self, request):
"""
Returns a `User` if a correct username and password have been supplied
using HTTP Basic authentication. Otherwise returns `None`.
"""
auth = get_authorization_header(request).split()
if not auth or auth[0].lower() != b'basic':
return None #返回none不處理。讓下一個處理
if len(auth) == 1:
msg = _('Invalid basic header. No credentials provided.')
raise exceptions.AuthenticationFailed(msg)
elif len(auth) > 2:
msg = _('Invalid basic header. Credentials string should not contain spaces.')
raise exceptions.AuthenticationFailed(msg)
try:
auth_parts = base64.b64decode(auth[1]).decode(HTTP_HEADER_ENCODING).partition(':') #用partition切割冒號也包括
except (TypeError, UnicodeDecodeError, binascii.Error):
msg = _('Invalid basic header. Credentials not correctly base64 encoded.')
raise exceptions.AuthenticationFailed(msg)
userid, password = auth_parts[0], auth_parts[2] # 返回用戶和密碼
return self.authenticate_credentials(userid, password, request)
def authenticate_credentials(self, userid, password, request=None):
"""
Authenticate the userid and password against username and password
with optional request for context.
"""
credentials = {
get_user_model().USERNAME_FIELD: userid,
'password': password
}
user = authenticate(request=request, **credentials)
if user is None:
raise exceptions.AuthenticationFailed(_('Invalid username/password.'))
if not user.is_active:
raise exceptions.AuthenticationFailed(_('User inactive or deleted.'))
return (user, None)
def authenticate_header(self, request):
return 'Basic realm="%s"' % self.www_authenticate_realm
```
**g、當然restfulframework默認定義了兩個類。我們也可以自定制類,自己有就用自己的了,自己沒有就去找父類的了,但是里面必須實現authenticate方法,不然會報錯。**
**2、加工完request之后的操作**
* 處理版權信息
* 認證
* 權限
* 請求用戶進行訪問頻率的限制
認證流程
**a、首先?self.initial(request, \*args, \*\*kwargs)可以看到做了以下操作**
```
def initial(self, request, *args, **kwargs):
"""
Runs anything that needs to occur prior to calling the method handler.
"""
self.format_kwarg = self.get_format_suffix(**kwargs)
# Perform content negotiation and store the accepted info on the request
neg = self.perform_content_negotiation(request)
request.accepted_renderer, request.accepted_media_type = neg
# Determine the API version, if versioning is in use.
#2.1 處理版本信息
version, scheme = self.determine_version(request, *args, **kwargs)
request.version, request.versioning_scheme = version, scheme
# Ensure that the incoming request is permitted
#2.2 認證
self.perform_authentication(request)
# 2.3 權限
self.check_permissions(request)
# 2.4 請求用戶進行訪問頻率的限制
self.check_throttles(request)
```
**?b、我們先來看認證,self.perform\_authentication(request) 具體干了什么,按住ctrl點擊進去**
```
def perform_authentication(self, request):
"""
Perform authentication on the incoming request.
Note that if you override this and simply 'pass', then authentication
will instead be performed lazily, the first time either
`request.user` or `request.auth` is accessed.
"""
request.user #執行request的user,這是的request已經是加工后的request了
```
**c、那么我們可以從視圖里面導入一下Request,找到request對象的user方法**
~~~
from rest_framework.views import Request
~~~
```
@property
def user(self):
"""
Returns the user associated with the current request, as authenticated
by the authentication classes provided to the request.
"""
if not hasattr(self, '_user'):
with wrap_attributeerrors():
self._authenticate() #
return self._user #返回user
```
**d、執行self.\_authenticate() 開始用戶認證,如果驗證成功后返回元組: (用戶,用戶Token)**
```
def _authenticate(self):
"""
Attempt to authenticate the request using each authentication instance
in turn.
"""
#循環對象列表
for authenticator in self.authenticators:
try:
#執行每一個對象的authenticate 方法
user_auth_tuple = authenticator.authenticate(self)
except exceptions.APIException:
self._not_authenticated()
raise
if user_auth_tuple is not None:
self._authenticator = authenticator
self.user, self.auth = user_auth_tuple #返回一個元組,user,和auth,賦給了self,
# 只要實例化Request,就會有一個request對象,就可以request.user,request.auth了
return
self._not_authenticated()
```
**e、在user\_auth\_tuple = authenticator.authenticate(self) 進行驗證,如果驗證成功,執行類里的authenticatie方法**?
**f、如果用戶沒有認證成功:self.\_not\_authenticated()**
```
def _not_authenticated(self):
"""
Set authenticator, user & authtoken representing an unauthenticated request.
Defaults are None, AnonymousUser & None.
"""
#如果跳過了所有認證,默認用戶和Token和使用配置文件進行設置
self._authenticator = None #
if api_settings.UNAUTHENTICATED_USER:
self.user = api_settings.UNAUTHENTICATED_USER() # 默認值為:匿名用戶AnonymousUser
else:
self.user = None # None 表示跳過該認證
if api_settings.UNAUTHENTICATED_TOKEN:
self.auth = api_settings.UNAUTHENTICATED_TOKEN() # 默認值為:None
else:
self.auth = None
# (user, token)
# 表示驗證通過并設置用戶名和Token;
# AuthenticationFailed異常
```
**3、執行get/post/delete等方法**
**4、對返回結果在進行加工**
### 二、自定義權限
在app目錄下創建一個rest\_utils的文件夾
```
from rest_framework.permissions import BasePermission
class MyPermission(BasePermission):
message = '必須是svip才能訪問'
def has_permission(self,request,view):
if request.user.user_type != 3:
return False
return True
permission.py
```
配置文件
~~~
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': ['app.rest_utils.permission.MyPermission', ],
}
~~~
如果有某些類不需要使用權限,可以在類中加上以下代碼
~~~
permission_classes = []
~~~
### 三、自定義節流
? 在app目錄下創建一個rest\_utils的文件夾
```
from rest_framework.throttling import BaseThrottle
import time
VISIT_RECORD = {}
class VisitThrottle(BaseThrottle):
def __init__(self):
self.history = None
def allow_request(self,request,view):
# 1. 獲取用戶IP
remote_addr = self.get_ident(request)
ctime = time.time()
if remote_addr not in VISIT_RECORD:
VISIT_RECORD[remote_addr] = [ctime,]
return True
history = VISIT_RECORD.get(remote_addr)
self.history = history
while history and history[-1] < ctime - 60:
history.pop()
if len(history) < 3:
history.insert(0,ctime)
return True
# return True # 表示可以繼續訪問
# return False # 表示訪問頻率太高,被限制
def wait(self):
# 還需要等多少秒才能訪問
ctime = time.time()
return 60 - (ctime - self.history[-1])
throttle.py
```
配置文件
~~~
REST_FRAMEWORK = {
"DEFAULT_THROTTLE_CLASSES": ["app.rest_utils.throttle.VisitThrottle"],
}
~~~
如果有某些類不需要使用節流,可以在類中加上以下代碼
~~~
throttle_classes = []
~~~
### 四、版本
配置文件
~~~
REST_FRAMEWORK = {
"DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.URLPathVersioning",
"DEFAULT_VERSION":'v1',
"ALLOWED_VERSIONS":['v1','v2'],
"VERSION_PARAM":'version',
}
~~~
路由
~~~
from django.conf.urls import url
from app.views import *
urlpatterns = [
url('^(?P<version>[v1|v2]+)/orderview/$',OrderView.as_view(),name='wusir'),
]
~~~
視圖
```
from django.http import JsonResponse
from rest_framework.views import APIView
from app.models import *
import hashlib
import time
def md5(user):
ctime = str(time.time())
m = hashlib.md5(bytes(user,encoding='utf-8'))
m.update(bytes(ctime,encoding='utf-8'))
return m.hexdigest()
class AuthView(APIView):
authentication_classes = []
permission_classes = []
def post(self,request,*args,**kwargs):
ret = {'code':1000,'msg':None}
try:
name = request._request.POST.get('username')
pwd = request._request.POST.get('password')
obj = UserInfo.objects.filter(username=name,password=pwd).first()
if not obj:
ret['code'] = 1001
ret['msg'] = '用戶名或密碼錯誤'
token = md5(name)
UserToken.objects.update_or_create(user=obj,defaults={'token':token})
ret['token'] = token
except Exception as e:
ret['code'] = 1002
ret['msg'] = '請求異常'
return JsonResponse(ret)
class OrderView(APIView):
def get(self,request,*args,**kwargs):
ret = {'code':1000,'msg':None,'data':None}
order = {'goods':'food'}
# 獲得版本信息
print(request.version)
# 獲取處理版本的對象
print(request.versioning_scheme)
#反向生成url(rest_framework)
u1 = request.versioning_scheme.reverse(viewname='wusir',request=request)
print(u1)
try:
ret['data'] = order
except Exception as e:
ret['msg'] = '有問題'
return JsonResponse(ret)
views.py
```
### 五、解析器
配置文件
~~~
REST_FRAMEWORK = {
"DEFAULT_PARSER_CLASSES": ['rest_framework.parsers.JSONParser', 'rest_framework.parsers.FormParser']
}
~~~
視圖函數中可以直接通過request.data拿到解析之后的數據
### 六、序列化
序列化共有兩個功能:1、數據序列化? 2、請求數據校驗
數據序列化
```
from django.db import models
class UserGroup(models.Model):
title = models.CharField(max_length=32)
class UserInfo(models.Model):
user_type_choices = (
(1,'普通用戶'),
(2,'VIP'),
(3,'SVIP'),
)
user_type = models.IntegerField(choices=user_type_choices)
username = models.CharField(max_length=32,unique=True)
password = models.CharField(max_length=64)
group = models.ForeignKey("UserGroup")
roles = models.ManyToManyField("Role")
class UserToken(models.Model):
user = models.OneToOneField(to='UserInfo')
token = models.CharField(max_length=64)
class Role(models.Model):
title = models.CharField(max_length=32)
models.py建表
```
```
import json
from rest_framework.views import APIView
from django.http import HttpResponse
from rest_framework import serializers
from app.models import *
#=========================================
# 自定義序列化類一
class RolesSerializers(serializers.Serializer):
id = serializers.IntegerField()
title = serializers.CharField()
class RoleViews(APIView):
def get(self,request,*args,**kwargs):
# 方式一 ,沒有使用數據序列化
res = Role.objects.all().values('title')
res = list(res)
res = json.dumps(res,ensure_ascii=False)
# 方式二 ,序列化多個數據[obj,obj,obj]
res = Role.objects.all()
ser = RolesSerializers(instance=res,many=True)
res = json.dumps(ser.data,ensure_ascii=False)
# 方式三 ,序列化單個數據 [obj]
res = Role.objects.all().first()
ser = RolesSerializers(instance=res,many=False)
res = json.dumps(ser.data,ensure_ascii=False)
return HttpResponse(res)
# =========================================
# +++++++++++++++++++++++++++++++++++++++++
# 自定義序列化類二
class UserInfoSerializers(serializers.Serializer):
# user_type = serializers.IntegerField()
aaaaa = serializers.IntegerField(source='user_type')
bbbbb = serializers.CharField(source='get_user_type_display')
username = serializers.CharField()
password = serializers.CharField()
gps = serializers.CharField(source='group.id')
# rls = serializers.CharField(source='roles.all') # 用這個拿到的是一個一個的對象
rls = serializers.SerializerMethodField() # 自定義顯示
def get_rls(self,row):
obj_list = row.roles.all()
ret = []
for i in obj_list:
ret.append({'id':i.id,'title':i.title})
return ret
# 自定義序列化類三
class UserInfoSerializers(serializers.ModelSerializer):
bbbbb = serializers.CharField(source='get_user_type_display')
group = serializers.CharField(source='group.title')
rls = serializers.SerializerMethodField()
def get_rls(self,row):
obj_list = row.roles.all()
ret = []
for i in obj_list:
ret.append({'id':i.id,'title':i.title})
return ret
class Meta:
model = UserInfo
# fields = '__all__'
fields = ['id','username','password','bbbbb','group','rls']
# 自定義序列化類四(深度化控制)
class UserInfoSerializers(serializers.ModelSerializer):
class Meta:
model = UserInfo
# fields = '__all__'
fields = ['id','username','password','user_type','group','roles']
depth = 1
# 自定義序列化類五(Hypermedia API)
class UserInfoSerializers(serializers.ModelSerializer):
group = serializers.HyperlinkedIdentityField(view_name='wusir',lookup_field='group_id',lookup_url_kwarg='pk')
class Meta:
model = UserInfo
# fields = '__all__'
fields = ['id','username','password','user_type','group','roles']
class UserInfoViews(APIView):
def get(self,request,*args,**kwargs):
users = UserInfo.objects.all()
ser = UserInfoSerializers(instance=users,many=True,context={'request':request})
res = json.dumps(ser.data,ensure_ascii=False)
return HttpResponse(res)
# GroupSerializers和GroupViews配合自定義序列化類五生成Hypermedia API后,點擊url可以看詳情
class GroupSerializers(serializers.ModelSerializer):
class Meta:
model = UserGroup
fields = '__all__'
class GroupViews(APIView):
def get(self,request,*args,**kwargs):
pk = kwargs.get('pk')
res = UserGroup.objects.filter(id=pk).first()
ser = GroupSerializers(instance=res,many=False)
res = json.dumps(ser.data,ensure_ascii=False)
return HttpResponse(res)
# +++++++++++++++++++++++++++++++++++++++++
views.py
```
```
from django.conf.urls import url
from app.views import *
urlpatterns = [
url('role/$',RoleViews.as_view()),
url('info/$',UserInfoViews.as_view()),
url('group/(?P<pk>\d+)$',GroupViews.as_view(),name='wusir'),
]
urls.py
```
請求數據校驗
```
class XXValidator(object):
def __init__(self, base):
self.base = base
def __call__(self, value):
if not value.startswith(self.base):
message = '標題必須以 %s 為開頭。' % self.base
raise serializers.ValidationError(message)
def set_context(self, serializer_field):
pass
class UserGroupSerializer(serializers.Serializer):
title = serializers.CharField(error_messages={'required': '標題不能為空'},validators=[XXValidator('wusir'),])
# required為錯誤時的提示信息,validators為自定義驗證器,可以自定義功能,也可以不寫,不寫的話只能校驗數據是否為空
class UserGroupView(APIView):
def post(self, request, *args, **kwargs):
ser = UserGroupSerializer(data=request.data)
if ser.is_valid():
print(ser.validated_data['title'])
else:
print(ser.errors)
return HttpResponse('提交數據')
views.py
```
~~~
from django.conf.urls import url
from app.views import *
urlpatterns = [
url('usergroup/$',UserGroupView.as_view()),
]
~~~
### 七、分頁
PS:先自定義一個序列化,然后在分頁程序中導入這個序列化程序,所有的原生分頁需要現在settings.py中定義一下每頁顯示幾條數據
~~~
REST_FRAMEWORK = {
"PAGE_SIZE":2,
}
~~~
A1、分頁,看第n頁,每頁顯示n條數據(原生)
```
from django.http import JsonResponse, HttpResponse
from rest_framework.pagination import PageNumberPagination
from rest_framework.views import APIView
from app.models import *
import hashlib
import time
from app.rest_utils.serializsers.pager import PagerSerialiser
from rest_framework.response import Response
class PageView(APIView):
authentication_classes = []
permission_classes = []
throttle_classes = []
def get(self,request,*args,**kwargs):
roles = Role.objects.all()
pg = PageNumberPagination()
page_roles = pg.paginate_queryset(queryset=roles,request=request,view=self)
ser = PagerSerialiser(instance=page_roles,many=True)
# return Response(ser.data)
return pg.get_paginated_response(ser.data) #頁面上加上上下頁的url
views.py
```
A2、分頁,看第n頁,每頁顯示n條數據(自定義)
```
from django.http import JsonResponse, HttpResponse
from rest_framework.pagination import PageNumberPagination
from rest_framework.views import APIView
from app.models import *
import hashlib
import time
from app.rest_utils.serializsers.pager import PagerSerialiser
from rest_framework.response import Response
class MyPageNumberPagination(PageNumberPagination):
page_size = 3 #默認每頁顯示個數
page_query_param = 'page' #get傳參表示第幾頁
page_size_query_param = 'pagesize' #get傳參表示每頁顯示幾個
max_page_size = 5 #每頁最大顯示個數
class PageView(APIView):
authentication_classes = []
permission_classes = []
throttle_classes = []
def get(self,request,*args,**kwargs):
roles = Role.objects.all()
pg = MyPageNumberPagination()
page_roles = pg.paginate_queryset(queryset=roles,request=request,view=self)
ser = PagerSerialiser(instance=page_roles,many=True)
# return Response(ser.data)
return pg.get_paginated_response(ser.data) #頁面上加上上下頁的url
views.py
```
B1. 分頁,在n個位置,向后查看n條數據(原生)
```
from django.http import JsonResponse, HttpResponse
from rest_framework.pagination import LimitOffsetPagination
from rest_framework.views import APIView
from app.models import *
import hashlib
import time
from app.rest_utils.serializsers.pager import PagerSerialiser
from rest_framework.response import Response
class PageView(APIView):
authentication_classes = []
permission_classes = []
throttle_classes = []
def get(self,request,*args,**kwargs):
roles = Role.objects.all()
pg = LimitOffsetPagination()
page_roles = pg.paginate_queryset(queryset=roles,request=request,view=self)
ser = PagerSerialiser(instance=page_roles,many=True)
# return Response(ser.data)
return pg.get_paginated_response(ser.data) #頁面上加上上下頁的url
views.py
```
B2. 分頁,在n個位置,向后查看n條數據(自定義)
```
from django.http import JsonResponse, HttpResponse
from rest_framework.pagination import LimitOffsetPagination
from rest_framework.views import APIView
from app.models import *
import hashlib
import time
from app.rest_utils.serializsers.pager import PagerSerialiser
from rest_framework.response import Response
class MyLimitOffsetPagination(LimitOffsetPagination):
default_limit = 3 #默認每頁顯示個數
limit_query_param = 'limit' #get傳參表示每頁顯示個數
offset_query_param = 'offset' #get傳參表示跳過幾個數據顯示
max_limit = 6 #每頁最大顯示個數
class PageView(APIView):
authentication_classes = []
permission_classes = []
throttle_classes = []
def get(self,request,*args,**kwargs):
roles = Role.objects.all()
pg = MyLimitOffsetPagination()
page_roles = pg.paginate_queryset(queryset=roles,request=request,view=self)
ser = PagerSerialiser(instance=page_roles,many=True)
# return Response(ser.data)
return pg.get_paginated_response(ser.data) #頁面上加上上下頁的url
views.py
```
C1.加密分頁,上一頁和下一頁(原生)
```
from django.http import JsonResponse, HttpResponse
from rest_framework.pagination import CursorPagination
from rest_framework.views import APIView
from app.models import *
import hashlib
import time
from app.rest_utils.serializsers.pager import PagerSerialiser
from rest_framework.response import Response
class PageView(APIView):
authentication_classes = []
permission_classes = []
throttle_classes = []
def get(self,request,*args,**kwargs):
roles = Role.objects.all()
pg = CursorPagination()
page_roles = pg.paginate_queryset(queryset=roles,request=request,view=self)
ser = PagerSerialiser(instance=page_roles,many=True)
# return Response(ser.data)
return pg.get_paginated_response(ser.data) #頁面上加上上下頁的url
views.py
```
C2.加密分頁,上一頁和下一頁(自定義)
```
from django.http import JsonResponse, HttpResponse
from rest_framework.pagination import CursorPagination
from rest_framework.views import APIView
from app.models import *
import hashlib
import time
from app.rest_utils.serializsers.pager import PagerSerialiser
from rest_framework.response import Response
class MyCursorPagination(CursorPagination):
cursor_query_param = 'wusir' #get參數中確定以什么值為key來接受下一頁的參數
page_size = 3 #默認每頁顯示數目
ordering = 'id' #根據什么字段來進行排序
page_size_query_param = 'pagesize' #get傳參表示每頁顯示幾個
max_page_size = 5 #每頁最大顯示數目
class PageView(APIView):
authentication_classes = []
permission_classes = []
throttle_classes = []
def get(self,request,*args,**kwargs):
roles = Role.objects.all()
pg = MyCursorPagination()
page_roles = pg.paginate_queryset(queryset=roles,request=request,view=self)
ser = PagerSerialiser(instance=page_roles,many=True)
# return Response(ser.data)
return pg.get_paginated_response(ser.data) #頁面上加上上下頁的url
views.py
```
### 八、視圖
PS:View和APIView在此處就不多介紹
GenericAPIView
```
from rest_framework.pagination import PageNumberPagination
from rest_framework.views import APIView
from app.models import *
from app.rest_utils.serializsers.pager import PagerSerialiser
from rest_framework.response import Response
from rest_framework.generics import GenericAPIView
class View1View(GenericAPIView):
authentication_classes = []
permission_classes = []
throttle_classes = []
queryset = Role.objects.all()
serializer_class = PagerSerialiser
pagination_class = PageNumberPagination
def get(self,request,*args,**kwargs):
# 獲取數據
roles = self.get_queryset()
# 分頁
pager_roles = self.paginate_queryset(roles)
# 序列化
ser = self.get_serializer(instance=pager_roles,many=True)
return Response(ser.data)
views.py
```
GenericViewSet(as\_view里面可以用字典的方式讓get,post等不同的方法對應執行不同名稱的函數)
```
from rest_framework.pagination import PageNumberPagination
from app.models import *
from rest_framework.response import Response
from app.rest_utils.serializsers.pager import PagerSerialiser
from rest_framework.viewsets import GenericViewSet
class View1View(GenericViewSet):
authentication_classes = []
permission_classes = []
throttle_classes = []
queryset = Role.objects.all()
serializer_class = PagerSerialiser
pagination_class = PageNumberPagination
def list(self, request, *args, **kwargs):
# 獲取數據
roles = self.get_queryset()
# 分頁
pager_roles = self.paginate_queryset(roles)
# 序列化
ser = self.get_serializer(instance=pager_roles, many=True)
return Response(ser.data)
views.py
```
~~~
from django.conf.urls import url
from app.views import *
urlpatterns = [
url(r'^view1view/$',View1View.as_view({'get':'list'})),
]
~~~
mixins.CreateModelMixin,mixins.RetrieveModelMixin,mixins.UpdateModelMixin,mixins.DestroyModelMixin,mixins.ListModelMixin,
(任意舉兩個栗子)
```
from app.rest_utils.serializsers.pager import PagerSerialiser
from rest_framework.viewsets import GenericViewSet,ModelViewSet
from rest_framework.mixins import ListModelMixin,CreateModelMixin
class View1View(GenericViewSet,ListModelMixin,CreateModelMixin):
authentication_classes = []
permission_classes = []
throttle_classes = []
queryset = Role.objects.all()
serializer_class = PagerSerialiser
pagination_class = PageNumberPagination
views.py
```
~~~
from django.conf.urls import url
from app.views import *
urlpatterns = [
url(r'^view1view/$',View1View.as_view({'get':'list','post':'create'})),
]
~~~
ModelViewSet
```
from app.rest_utils.serializsers.pager import PagerSerialiser
from rest_framework.viewsets import ModelViewSet
class View1View(ModelViewSet):
authentication_classes = []
permission_classes = []
throttle_classes = []
queryset = Role.objects.all()
serializer_class = PagerSerialiser
pagination_class = PageNumberPagination
views.py
```
```
from django.conf.urls import url
from app.views import *
urlpatterns = [
url(r'^view1view/$',View1View.as_view({'get': 'list','post':'create'})),
url(r'^view1view/(?P<pk>\d+)/$',View1View.as_view({'get': 'retrieve','delete':'destroy','put':'update','patch':'partial_update'})),
]
urls.py
```
繼承關系:APIView繼承了View,GenericAPIView繼承了APIView,GenericViewSet繼承了GenericAPIView和ViewSetMixin,ModelViewSet繼承了mixins.CreateModelMixin,mixins.RetrieveModelMixin,mixins.UpdateModelMixin,mixins.DestroyModelMixin,mixins.ListModelMixin和GenericViewSet。
### 九、路由系統
當繼承了ModelViewSet之后,一個視圖對于的完整的路由就有以下四種
```
from django.conf.urls import url
from app.views import *
urlpatterns = [
# http://127.0.0.1:8000/api/v1/v1/?format=json
url(r'^(?P<version>[v1|v2]+)/view1view/$', View1View.as_view({'get': 'list','post':'create'})),
# http://127.0.0.1:8000/api/v1/v1.json
url(r'^(?P<version>[v1|v2]+)/view1view\.(?P<format>\w+)$', View1View.as_view({'get': 'list','post':'create'})),
url(r'^(?P<version>[v1|v2]+)/view1view/(?P<pk>\d+)/$', View1View.as_view({'get': 'retrieve','delete':'destroy','put':'update','patch':'partial_update'})),
url(r'^(?P<version>[v1|v2]+)/view1view/(?P<pk>\d+)\.(?P<format>\w+)$', View1View.as_view({'get': 'retrieve','delete':'destroy','put':'update','patch':'partial_update'})),
]
urls.py
```
這時候我們可以使用rest\_framework給我們自動生成路由的方式來生成以上四種路由,效果相同
```
from django.conf.urls import url, include
from app.views import *
from rest_framework import routers
router = routers.DefaultRouter()
router.register(r'view1view',View1View)
urlpatterns = [
url(r'^(?P<version>[v1|v2]+)/', include(router.urls)),
]
urls.py
```
### 十、渲染器
只需要在配置文件中配置一下所需要的渲染器類型就可以了
~~~
REST_FRAMEWORK = {
"DEFAULT_RENDERER_CLASSES":[
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.BrowsableAPIRenderer',
]
}
~~~
- Python學習
- Python基礎
- Python初識
- 列表生成式,生成器,可迭代對象,迭代器詳解
- Python面向對象
- Python中的單例模式
- Python變量作用域、LEGB、閉包
- Python異常處理
- Python操作正則
- Python中的賦值與深淺拷貝
- Python自定義CLI三方庫
- Python并發編程
- Python之進程
- Python之線程
- Python之協程
- Python并發編程與IO模型
- Python網絡編程
- Python之socket網絡編程
- Django學習
- 反向解析
- Cookie和Session操作
- 文件上傳
- 緩存的配置和使用
- 信號
- FBV&&CBV&&中間件
- Django補充
- 用戶認證
- 分頁
- 自定義搜索組件
- Celery
- 搭建sentry平臺監控
- DRF學習
- drf概述
- Flask學習
- 項目拆分
- 三方模塊使用
- 爬蟲學習
- Http和Https區別
- 請求相關庫
- 解析相關庫
- 常見面試題
- 面試題
- 面試題解析
- 網絡原理
- 計算機網絡知識簡單介紹
- 詳解TCP三次握手、四次揮手及11種狀態
- 消息隊列和數據庫
- 消息隊列之RabbitMQ
- 數據庫之Redis
- 數據庫之初識MySQL
- 數據庫之MySQL進階
- 數據庫之MySQL補充
- 數據庫之Python操作MySQL
- Kafka常用命令
- Linux學習
- Linux基礎命令
- Git
- Git介紹
- Git基本配置及理論
- Git常用命令
- Docker
- Docker基本使用
- Docker常用命令
- Docker容器數據卷
- Dockerfile
- Docker網絡原理
- docker-compose
- Docker Swarm
- HTML
- CSS
- JS
- VUE