赞
踩
为前端提供URL(API/接口的开发)
注:永远返回HttpResponse
# FBV(function base view)
def users(request):
user_list = ['alex','oldboy']
return HttpResponse(json.dumps((user_list)))
# CBV(class base view) # 路由: url(r'^students/', views.StudentsView.as_view()), # 视图: from django.views import View class StudentsView(View): def get(self,request,*args,**kwargs): return HttpResponse('GET') def post(self, request, *args, **kwargs): return HttpResponse('POST') def put(self, request, *args, **kwargs): return HttpResponse('PUT') def delete(self, request, *args, **kwargs): return HttpResponse('DELETE')
CBV,基于反射实现根据请求方式不同,执行不同的方法。
原理:
url -> view方法 -> dispatch方法(反射执行其他:GET/POST/DELETE/PUT)
流程:
class StudentsView(View): def dispatch(self, request, *args, **kwargs): print('before') ret = super(StudentsView,self).dispatch(request, *args, **kwargs) print('after') return ret def get(self,request,*args,**kwargs): return HttpResponse('GET') def post(self, request, *args, **kwargs): return HttpResponse('POST') def put(self, request, *args, **kwargs): return HttpResponse('PUT') def delete(self, request, *args, **kwargs): return HttpResponse('DELETE')
【扩展】:
super()方法,
super 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是如果使用多继承,会涉及到查找顺序(MRO)、重复调用(钻石继承)等种种问题。总之前人留下的经验就是:保持一致性。要不全部用类名调用父类,要不就全部用 super,不要一半一半
中间件顾名思义,是介于request与response处理之间的一道处理过程,相对比较轻量级,并且在全局上改变django的输入与输出
- 中间件流程图
# 重要的四个方法
process_request(self, request)
process_view(self, request, view, *args, **kwargs)
process_exception(self, request, exception)
process_response(self, request, response)
# 模板处理的方法
process_template_response
- 使用中间件做过什么?
- 权限
- 做IP访问频率限制
- 某些IP访问服务器的频率过高,进行拦截,比如限制每分钟不能超过20次。
- 用户登录认证
- django的csrf是如何实现?
process_view方法
- 检查视图是否被 @csrf_exempt (免除csrf_token认证)
- 去请求体或cookie中获取token
FBV,csrf_token认证情况
情况一:所有函数都要认证,某一个不需要认证 (@csrf_exempt)
MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', # 全站使用csrf认证 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] from django.views.decorators.csrf import csrf_exempt @csrf_exempt # 该函数无需认证 def users(request): user_list = ['tom','jeck'] return HttpResponse(json.dumps((user_list)))
情况二:所有都不认证,某个函数需要认证(@csrf_protect)
MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', #'django.middleware.csrf.CsrfViewMiddleware', # 全站不使用csrf认证 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] from django.views.decorators.csrf import csrf_exempt @csrf_protect # 该函数需认证 def users(request): user_list = ['tom','jeck'] return HttpResponse(json.dumps((user_list)))
CBV,csrf_token认证时需要使用
# 方式一: from django.views.decorators.csrf import csrf_exempt,csrf_protect from django.utils.decorators import method_decorator class StudentsView(View): @method_decorator(csrf_exempt) def dispatch(self, request, *args, **kwargs): return super(StudentsView,self).dispatch(request, *args, **kwargs) def get(self,request,*args,**kwargs): print('get方法') return HttpResponse('GET') def post(self, request, *args, **kwargs): return HttpResponse('POST') def put(self, request, *args, **kwargs): return HttpResponse('PUT') def delete(self, request, *args, **kwargs): return HttpResponse('DELETE') # 方式二: from django.views.decorators.csrf import csrf_exempt,csrf_protect from django.utils.decorators import method_decorator @method_decorator(csrf_exempt,name='dispatch') class StudentsView(View): def get(self,request,*args,**kwargs): print('get方法') return HttpResponse('GET') def post(self, request, *args, **kwargs): return HttpResponse('POST') def put(self, request, *args, **kwargs): return HttpResponse('PUT') def delete(self, request, *args, **kwargs): return HttpResponse('DELETE')
RESTful主要是面向资源(视网络上任何东西都是资源)设计它的接口的, 它有四个规范:
- 每个资源都存在唯一的标识URI
- 每个资源都具有四个动作谓词, 分别是GET/POST/PUT/DELETE
- 每次的动作都是无状态的, 即是HTTP的短连接(Connection: close|keep-alive)
- 交互的资源数据类型一般是json或xml.
安装
pip3 install djangorestframework
url.py
from django.urls import path, include
from web.views.api import TestView
urlpatterns = [
path('test/', TestView.as_view),
]
views.py
from rest_framework.views import APIView from rest_framework.response import Response class TestView(APIView): def dispatch(self, request, *args, **kwargs): """ 请求到来之后,都要执行dispatch方法,dispatch方法根据请求方式不同触发 get/post/put等方法 注意:APIView中的dispatch方法有好多好多的功能 """ return super().dispatch(request, *args, **kwargs) def get(self, request, *args, **kwargs): return Response('GET请求,响应内容') def post(self, request, *args, **kwargs): return Response('POST请求,响应内容') def put(self, request, *args, **kwargs): return Response('PUT请求,响应内容')
局部&全局设置
... 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( # request, # parsers=self.get_parsers(), # authenticators=self.get_authenticators(), # negotiator=self.get_content_negotiator(), # parser_context=parser_context ) # request(原生的request, [BasicAuthentication对象,]) \ # 获取原生request, 用request._request # 获取认证类的对象, request.authentications # 1.封装request request = self.initialize_request(request, *args, **kwargs) self.request = request self.headers = self.default_response_headers # deprecate? try: # 2.认证 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 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
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. version, scheme = self.determine_version(request, *args, **kwargs) request.version, request.versioning_scheme = version, scheme # Ensure that the incoming request is permitted # 3.实现认证 self.perform_authentication(request) # 4.权限判断 self.check_permissions(request) # 5.访问频率 self.check_throttles(request)
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
@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
def _authenticate(self): """ Attempt to authenticate the request using each authentication instance in turn. """ # 循环认证对象[BasicAuthentication对象,] for authenticator in self.authenticators: try: # 执行认证类的authenticate方法 # ① 如果authenticate方法抛出异常,self._not_authenticated()执行 # ② 有返回值,必须是元组(self.user, self.auth) # ③ 返回None,我不管,下一个认证 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 return self._not_authenticated()
def _not_authenticated(self): """ Set authenticator, user & authtoken representing an unauthenticated request. Defaults are None, AnonymousUser & None. """ self._authenticator = None if api_settings.UNAUTHENTICATED_USER: self.user = api_settings.UNAUTHENTICATED_USER() # AnonymousUser 设置默认值表示匿名用户 else: self.user = None if api_settings.UNAUTHENTICATED_TOKEN: self.auth = api_settings.UNAUTHENTICATED_TOKEN() # None else: self.auth = None
def check_permissions(self, request):
"""
Check if the request should be permitted.
Raises an appropriate exception if the request is not permitted.
"""
# 遍历[权限类的对象,权限类的对象,]
for permission in self.get_permissions():
if not permission.has_permission(request, self):
self.permission_denied(
request,
message=getattr(permission, 'message', None),
code=getattr(permission, 'code', None)
)
权限类对象:
def get_permissions(self):
"""
Instantiates and returns the list of permissions that this view requires.
"""
return [permission() for permission in self.permission_classes] # 列表生成式
如果上面permission.has_permission(request, self):
为False
def permission_denied(self, request, message=None, code=None):
"""
If request is not permitted, determine what kind of exception to raise.
"""
if request.authenticators and not request.successful_authenticator:
raise exceptions.NotAuthenticated()
raise exceptions.PermissionDenied(detail=message, code=code) # 抛出异常 表示权限验证失败
def check_throttles(self, request): """ Check if request should be throttled. Raises an appropriate exception if the request is throttled. """ throttle_durations = [] for throttle in self.get_throttles(): if not throttle.allow_request(request, self): throttle_durations.append(throttle.wait()) if throttle_durations: # Filter out `None` values which may happen in case of config / rate # changes, see #1438 durations = [ duration for duration in throttle_durations if duration is not None ] duration = max(durations, default=None) self.throttled(request, duration)
def get_throttles(self):
"""
Instantiates and returns the list of throttles that this view uses.
"""
return [throttle() for throttle in self.throttle_classes]
from django.views import View from rest_framework.views import APIView from rest_framework.authentication import BasicAuthentication from rest_framework import exceptions from rest_framework.request import Request class MyAuthentication(object): def authenticate(self,request): token = request._request.GET.get('token') # 获取用户名和密码,去数据校验 if not token: raise exceptions.AuthenticationFailed('用户认证失败') return ("zhb",None) def authenticate_header(self,val): pass class DogView(APIView): authentication_classes = [MyAuthentication,] def get(self,request,*args,**kwargs): print(request) print(request.user) # 拿到上面authenticate方法返回值元组里的第一个元素"zhb" # self.dispatch # 源码流程入口 ret = { 'code':1000, 'msg':'xxx' } return HttpResponse(json.dumps(ret),status=201) def post(self,request,*args,**kwargs): return HttpResponse('创建Dog') def put(self,request,*args,**kwargs): return HttpResponse('更新Dog') def delete(self,request,*args,**kwargs): return HttpResponse('删除Dog')
urls.py文件:
from django.conf.urls import url
from django.contrib import admin
from api import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^api/v1/auth/$', views.AuthView.as_view()),
url(r'^api/v1/order/$', views.OrderView.as_view()),
url(r'^api/v1/info/$', views.UserInfoView.as_view()),
]
model.py文件 (创建两张表)
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)
settings.py 文件里面全局设置认证,视图里面则不需要设置
REST_FRAMEWORK = {
# 全局使用的认证类
"DEFAULT_AUTHENTICATION_CLASSES":['api.utils.auth.FirstAuthtication','api.utils.auth.Authtication', ],
# "DEFAULT_AUTHENTICATION_CLASSES":['api.utils.auth.FirstAuthtication', ],
# "UNAUTHENTICATED_USER":lambda :"匿名用户"
"UNAUTHENTICATED_USER":None, # 匿名,request.user = None
"UNAUTHENTICATED_TOKEN":None,# 匿名,request.auth = None
"DEFAULT_PERMISSION_CLASSES":['api.utils.permission.SVIPPermission'], # 权限判断全局设置
"DEFAULT_THROTTLE_CLASSES":["api.utils.throttle.UserThrottle"],
"DEFAULT_THROTTLE_RATES":{
"Luffy":'3/m', # 匿名用户每分钟3次
"LuffyUser":'10/m', # 登录用户没分中10次
}
}
api–>views.py
from django.shortcuts import render,HttpResponse from django.http import JsonResponse from rest_framework.views import APIView from rest_framework.request import Request from rest_framework import exceptions from rest_framework.authentication import BasicAuthentication from api.utils.permission import SVIPPermission from api.utils.permission import MyPermission1 from api.utils.throttle import VisitThrottle from api import models ORDER_DICT = { 1:{ 'name': "媳妇", 'age':18, 'gender':'男', 'content':'...' }, 2:{ 'name': "老狗", 'age':19, 'gender':'男', 'content':'...。。' }, } def md5(user): import hashlib import time 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 = [] throttle_classes = [VisitThrottle,] def post(self,request,*args,**kwargs): ret = {'code':1000,'msg':None} try: user = request._request.POST.get('username') pwd = request._request.POST.get('password') obj = models.UserInfo.objects.filter(username=user,password=pwd).first() if not obj: ret['code'] = 1001 ret['msg'] = "用户名或密码错误" # 为登录用户创建token token = md5(user) # 存在就更新,不存在就创建 models.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): """ 订单相关业务(只有SVIP用户有权限) """ def get(self,request,*args,**kwargs): # request.user # request.auth self.dispatch ret = {'code':1000,'msg':None,'data':None} try: ret['data'] = ORDER_DICT except Exception as e: pass return JsonResponse(ret) class UserInfoView(APIView): """ 订单相关业务(普通用户、VIP) """ permission_classes = [MyPermission1, ] def get(self,request,*args,**kwargs): return HttpResponse('用户信息')
api --> utils --> auth.py 将认证类抽取出来
from rest_framework import exceptions from api import models from rest_framework.authentication import BaseAuthentication class FirstAuthtication(BaseAuthentication): def authenticate(self,request): pass def authenticate_header(self, request): pass class Authtication(BaseAuthentication): def authenticate(self,request): token = request._request.GET.get('token') token_obj = models.UserToken.objects.filter(token=token).first() if not token_obj: raise exceptions.AuthenticationFailed('用户认证失败') # 在rest framework内部会将整个两个字段赋值给request,以供后续操作使用 return (token_obj.user, token_obj) def authenticate_header(self, request): return 'Basic realm="api"'
a. 问题:有些API需要用户登录成功之后,才能访问;有些无需登录就能访问。
b. 基本使用认证组件
解决:
1. 创建两张表
2. 用户登录(返回token并保存到数据库)
c. 认证流程原理
- 上面源码
d. 再看一遍源码
1. 局部视图使用&全局使用
2. 匿名是request.user = None
e. 内置认证类
1. 认证类,必须继承:from rest_framework.authentication import BaseAuthentication
2. 其他认证类:BasicAuthentication
梳理:
1. 使用 - 创建类:继承BaseAuthentication; 实现:authenticate方法 - 返回值: - None,我不管了,下一认证来执行。 - raise exceptions.AuthenticationFailed('用户认证失败') # from rest_framework import exceptions - (元素1,元素2) # 元素1赋值给request.user; 元素2赋值给request.auth - 局部使用 from rest_framework.authentication import BaseAuthentication,BasicAuthentication class UserInfoView(APIView): """ 订单相关业务 """ authentication_classes = [BasicAuthentication,] def get(self,request,*args,**kwargs): print(request.user) return HttpResponse('用户信息') - 全局使用: REST_FRAMEWORK = { # 全局使用的认证类 "DEFAULT_AUTHENTICATION_CLASSES:['api.utils.auth.FirstAuthtication','api.utils.auth.Authtication', ], # "UNAUTHENTICATED_USER":lambda :"匿名用户" "UNAUTHENTICATED_USER":None, # 匿名,request.user = None "UNAUTHENTICATED_TOKEN":None,# 匿名,request.auth = None } 2. 源码流程 - dispatch - 封装request - 获取定义的认证类(全局/局部),通过列表生成时创建对象。 - initial - perform_authentication request.user(内部循环....)
api --> utils --> permission.py 将权限判断类抽取出来
from rest_framework.permissions import BasePermission
class SVIPPermission(BasePermission): # 继承BasePermission(内置类方法)
message = "必须是SVIP才能访问"
def has_permission(self,request,view):
if request.user.user_type != 3:
return False
return True
class MyPermission1(BasePermission):
def has_permission(self,request,view):
if request.user.user_type == 3:
return False
return True
1. 使用 - 类,必须继承:BasePermission,必须实现:has_permission方法 from rest_framework.permissions import BasePermission class SVIPPermission(BasePermission): message = "必须是SVIP才能访问" def has_permission(self,request,view): if request.user.user_type != 3: return False return True - 返回值: - True, 有权访问 - False,无权访问 - 局部 class UserInfoView(APIView): """ 订单相关业务(普通用户、VIP) """ permission_classes = [MyPermission1, ] def get(self,request,*args,**kwargs): return HttpResponse('用户信息') - 全局 REST_FRAMEWORK = { "DEFAULT_PERMISSION_CLASSES":['api.utils.permission.SVIPPermission'] } 2. 源码流程 ...
api --> utils --> throttle.py 将访问频率类抽取出来
自己实现:
import time VISIT_RECORD = {} class VisitThrottle(BaseThrottle): """ 60S内只能访问3次 """ 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 # 当前IP对应的列表中的最后一个时间记录<当前时间减60秒,即60S前的时间删除 while history and history[-1] < ctime - 60: history.pop() # 判断当前IP对应的列表中个数<3个,则把当前时间插入列表 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])
使用Django内置类
from rest_framework.throttling import BaseThrottle,SimpleRateThrottle
class VisitThrottle(SimpleRateThrottle):
scope = "Luffy"
def get_cache_key(self, request, view):
return self.get_ident(request)
class UserThrottle(SimpleRateThrottle):
scope = "LuffyUser"
def get_cache_key(self, request, view):
return request.user.username
SimpleRateThrottle 类里面实现的方法(源码):
主要是allow_request()
wait()
方法
class SimpleRateThrottle(BaseThrottle): """ A simple cache implementation, that only requires `.get_cache_key()` to be overridden. The rate (requests / seconds) is set by a `rate` attribute on the View class. The attribute is a string of the form 'number_of_requests/period'. Period should be one of: ('s', 'sec', 'm', 'min', 'h', 'hour', 'd', 'day') Previous request information used for throttling is stored in the cache. """ cache = default_cache timer = time.time cache_format = 'throttle_%(scope)s_%(ident)s' scope = None THROTTLE_RATES = api_settings.DEFAULT_THROTTLE_RATES def __init__(self): if not getattr(self, 'rate', None): self.rate = self.get_rate() self.num_requests, self.duration = self.parse_rate(self.rate) def get_cache_key(self, request, view): """ Should return a unique cache-key which can be used for throttling. Must be overridden. May return `None` if the request should not be throttled. """ raise NotImplementedError('.get_cache_key() must be overridden') def get_rate(self): """ Determine the string representation of the allowed request rate. """ if not getattr(self, 'scope', None): msg = ("You must set either `.scope` or `.rate` for '%s' throttle" % self.__class__.__name__) raise ImproperlyConfigured(msg) try: return self.THROTTLE_RATES[self.scope] except KeyError: msg = "No default throttle rate set for '%s' scope" % self.scope raise ImproperlyConfigured(msg) def parse_rate(self, rate): """ Given the request rate string, return a two tuple of: <allowed number of requests>, <period of time in seconds> """ if rate is None: return (None, None) num, period = rate.split('/') num_requests = int(num) duration = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}[period[0]] return (num_requests, duration) def allow_request(self, request, view): """ Implement the check to see if the request should be throttled. On success calls `throttle_success`. On failure calls `throttle_failure`. """ if self.rate is None: return True self.key = self.get_cache_key(request, view) if self.key is None: return True self.history = self.cache.get(self.key, []) self.now = self.timer() # Drop any requests from the history which have now passed the # throttle duration while self.history and self.history[-1] <= self.now - self.duration: self.history.pop() if len(self.history) >= self.num_requests: return self.throttle_failure() return self.throttle_success() def throttle_success(self): """ Inserts the current request's timestamp along with the key into the cache. """ self.history.insert(0, self.now) self.cache.set(self.key, self.history, self.duration) return True def throttle_failure(self): """ Called when a request to the API has failed due to throttling. """ return False def wait(self): """ Returns the recommended next request time in seconds. """ if self.history: remaining_duration = self.duration - (self.now - self.history[-1]) else: remaining_duration = self.duration available_requests = self.num_requests - len(self.history) + 1 if available_requests <= 0: return None return remaining_duration / float(available_requests)
DRF,是django第三方组件,能帮助我们快速实现遵循rest ful 规范的接口。
本质是一个Django的组件(app);
帮助我们快速实现遵循rest ful 规范的接口;
- 帮助我们做了csrf的豁免
- 页面渲染(将json放到页面了)
- 序列化(直接对QuerySet对象序列化)
- request.data (拿到用户数据并反序列化)
# 安装
pip install djangorestframework
使用drf开始restful API :
在settings的app中注册:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework'
]
直接对QuerySet对象序列化
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7cLfX6qA-1610934599975)(C:\Users\zhanghaibo04\AppData\Roaming\Typora\typora-user-images\image-20210113154607228.png)]
【扩展】:视图继承的类:
APIView: 没有提供增删改查功能,全部需要自己写。
ModelViewSet: 内部实现了增删改查,无需自己写。
url.py
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eA50QCb6-1610934599977)(C:\Users\zhanghaibo04\AppData\Roaming\Typora\typora-user-images\image-20210113162942561.png)]
view.py 视图函数实现(也需要 定义上面的实例化类)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9xjfQK2j-1610934599978)(C:\Users\zhanghaibo04\AppData\Roaming\Typora\typora-user-images\image-20210113162825955.png)]
局部控制,只能某个接口能获取到值
路由:
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^api/(?P<version>\w+)/', include('api.urls')), # api/ 后设置版本
]
urlpatterns = [
url(r'^order/$', views.OrderView.as_view()),
]
导入模块及视图函数:
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import QueryParameterVersioning,URLPathVersioning # 导入版本模块
class OrderView(APIView):
# 原本 versioning_class=None,此处重写versioning_class
versioning_class = URLPathVersioning
def get(self,request,*args,**kwargs):
print(request.version) # request.version获取版本
return Response('...')
全局控制,多有接口都能能获取到值
在settings.py中配置如下REST_FRAMEWORK
代码:
REST_FRAMEWORK = {
'DEFAULT_VERSIONING_CLASS': "rest_framework.versioning.URLPathVersioning"
}
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^api/(?P<version>\w+)/', include('api.urls')),
]
urlpatterns = [
url(r'^order/$', views.OrderView.as_view()),
]
from rest_framework.views import APIView
from rest_framework.response import Response
class OrderView(APIView):
def get(self,request,*args,**kwargs):
print(request.version)
return Response('...')
视图继承类的三种情况:
直接继承APIView,适用于非ORM简单操作(即不操作数据库),需要自定义功能时,使用。
from rest_framework.views import APIView
class TestView(APIView):
def get(self,request,*args,**kwargs):
pass
继承ListAPIView,DestroyAPIView,UpdateAPIView,RetrieveAPIView,CreateAPIView,项目中只要实现某几个接口时,而不是增删改查。
from api import models
from rest_framework import serializers
from rest_framework.generics import ListAPIView,DestroyAPIView,UpdateAPIView,RetrieveAPIView,CreateAPIView
class CourseSerializers(serializers.ModelSerializer):
class Meta:
model = models.Course
fields = "__all__"
class CourseView(ListAPIView,CreateAPIView): # 只实现增加功能
queryset = models.Course.objects.all()
serializer_class = CourseSerializers
继承ModelViewSet,功能中需要实现对表的增删改查时。
from rest_framework import serializers
from rest_framework.viewsets import ModelViewSet
class CourseSerializers(serializers.ModelSerializer):
class Meta:
model = models.Course
fields = "__all__"
class CourseView(ModelViewSet):
queryset = models.Course.objects.all()
serializer_class = CourseSerializers
面试题:GenericAPIView的作用?
指定了接口执行的流程。
如果继承了GenericAPIView的类,他们的在内部取数据时,调用 self.get_queryset()它定义在GenericAPIView,它内部返回self.queryset
创建zhb项目,5 张表
from django.db import models class Tag(models.Model): """ 标签表 """ caption = models.CharField(verbose_name='标签名称',max_length=32) class Course(models.Model): """ 课程表,例如:Linux、Python、测试课程 """ title = models.CharField(verbose_name='课程名称',max_length=32) class Module(models.Model): """ 模块表 """ name = models.CharField(verbose_name='模块名',max_length=32) course = models.ForeignKey(verbose_name='课程', to='Course', on_delete=models.CASCADE) class Video(models.Model): """ 视频表 """ title = models.CharField(verbose_name='视频名称', max_length=32) vid = models.CharField(verbose_name='xxx视频ID', max_length=64) tag = models.ManyToManyField(verbose_name='标签',to='Tag',)
实现接口返回一个字符串(含有版本)
# settings 中注册 INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'api.apps.ApiConfig', 'rest_framework', ] # settings中配置版本 REST_FRAMEWORK = { 'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning', 'ALLOWED_VERSIONS': ['v1', 'v2'] # 允许版本 }
urlpatterns = [
path('admin/', admin.site.urls),
re_path('api/(?P<version>\w+)/test/', views.TestView.as_view()),
]
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import URLPathVersioning
class TestView(APIView):
def get(self, request, *args, **kwargs):
print(request.version)
return Response('成功')
访问接口时,返回一个XML文件
<cross-domain-policy>
<allow-access-from domain="*.polyv.net"/>
</cross-domain-policy>
urlpatterns = [
path('admin/', admin.site.urls),
re_path('api/(?P<version>\w+)/test/', views.TestView.as_view()),
re_path('api/(?P<version>\w+)/crossdomain/', views.CrossView.as_view()),
]
class CrossView(APIView):
def get(self, request, *args, **kwargs):
with open('crossdomain.xml') as f:
data = f.read()
return Response(data)
实现对课程表的:
获取所有的数据
增加数据
urlpatterns = [
path('admin/', admin.site.urls),
re_path('api/(?P<version>\w+)/test/', views.TestView.as_view()),
re_path('api/(?P<version>\w+)/cross/', views.CrossView.as_view()),
re_path('api/(?P<version>\w+)/course/', views.CourseView.as_view()),
]
# 新建ser.py 用作新建序列化类
from api import models
from rest_framework import serializers
class CourseSerializer(serializers.ModelSerializer):
class Meta:
model = models.Course
fields = "__all__"
# view函数 from api import models from api.ser import CourseSerializer from rest_framework.views import APIView from rest_framework.response import Response class CourseView(APIView): def get(self, request, *args, **kwargs): result = {'status':1000, 'data':None, 'error':None} queryset = models.Course.objects.all() ser = CourseSerializer(instance=queryset, many=True) result['data'] = ser.data return Response(result) def post(self, request, *args, **kwargs): # 1.获取用户提交的数据 request.data # 2.校验数据的合法性 序列化 # 3.校验通过save # 4.不通过报错 result = {'status':1000, 'data':None, 'error':None} ser = CourseSerializer(data=request.data) if ser.is_valid(): ser.save() return Response(result) result['error'] = ser.errors result['status'] = 2000 return Response(result)
实现5.4功能
urlpatterns = [
path('admin/', admin.site.urls),
re_path('api/(?P<version>\w+)/test/', views.TestView.as_view()),
re_path('api/(?P<version>\w+)/cross/', views.CrossView.as_view()),
re_path('api/(?P<version>\w+)/course/$', views.CourseView.as_view()),
re_path('api/(?P<version>\w+)/course/new/$', views.CourseNewView.as_view()),
]
from api import models
from api.ser import CourseSerializer
from rest_framework.response import Response
from rest_framework.generics import ListAPIView, CreateAPIView
class CourseNewView(ListAPIView,CreateAPIView):
queryset = models.Course.objects.all()
serializer_class = CourseSerializer
urlpatterns = [
path('admin/', admin.site.urls),
re_path('api/(?P<version>\w+)/test/', views.TestView.as_view()),
re_path('api/(?P<version>\w+)/cross/', views.CrossView.as_view()),
re_path('api/(?P<version>\w+)/course/$', views.CourseView.as_view()),
re_path('api/(?P<version>\w+)/course/new/$', views.CourseNewView.as_view()),
re_path('api/(?P<version>\w+)/course/curd/$', views.CourseCurdView.as_view({'get': 'list', 'post': 'create'})),
re_path('api/(?P<version>\w+)/course/curd/(?P<pk>\d+)/$', views.CourseCurdView.as_view({'get':'retrieve','put':'update','delete':'destroy','patch':'partial_update'})),
]
from api import models
from api.ser import CourseSerializer
from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet
class CourseCurdView(ModelViewSet):
queryset = models.Course.objects.all()
serializer_class = CourseSerializer
对Course表实现增删改查,对列表页面的功能不要再去数据库获取,而去文件中获取即可
urlpatterns = [
path('admin/', admin.site.urls),
re_path('api/(?P<version>\w+)/test/', views.TestView.as_view()),
re_path('api/(?P<version>\w+)/cross/', views.CrossView.as_view()),
re_path('api/(?P<version>\w+)/course/$', views.CourseView.as_view()),
re_path('api/(?P<version>\w+)/course/new/$', views.CourseNewView.as_view()),
re_path('api/(?P<version>\w+)/course/curd/$', views.CourseCurdView.as_view({'get': 'list', 'post': 'create'})),
re_path('api/(?P<version>\w+)/course/curd/(?P<pk>\d+)/$', views.CourseCurdView.as_view({'get':'retrieve','put':'update','delete':'destroy','patch':'partial_update'})),
re_path('api/(?P<version>\w+)/course/file/$', views.CourseFileView.as_view({'get': 'list', 'post': 'create'})),
re_path('api/(?P<version>\w+)/course/file/(?P<pk>\d+)/$', views.CourseFileView.as_view({'get':'retrieve','put':'update','delete':'destroy','patch':'partial_update'})),
class CourseFileView(ModelViewSet):
queryset = models.Course.objects.all()
serializer_class = CourseSerializer
# 重写list方法,改变原有的功能
def list(self, request, *args, **kwargs):
with open('crossdomain.xml') as f:
data = f.read()
return Response(data)
基于APIView + serializer实现对模块表 实现获取数据 【ForeignKey】
显示所有数据
class ModuleSerializer(serializers.ModelSerializer):
class Meta:
model = models.Module
fields = "__all__"
class ModuleView(APIView):
def get(self, request, *args, **kwargs):
queryset = models.Module.objects.all()
ser = ModuleSerializer(instance=queryset, many=True)
return Response(ser.data)
只显示id和name
只修改自定义序列化类ModuleSerializer即可如下:
class ModuleSerializer(serializers.ModelSerializer):
class Meta:
model = models.Module
# fields = "__all__"
# fields = ['id', 'name'] # 显示想要显示的字段
exclude = ['course'] # 排除掉course
显示id、name和课程名称
class ModuleSerializer(serializers.ModelSerializer):
cname = serializers.CharField(source='course.title') # 定义连表需要显示的字段
class Meta:
model = models.Module
# fields = "__all__"
fields = ['id', 'name', 'cname'] # 显示想要显示的字段
显示id、name、课程名称和级别
# 修改表结构,模块表添加级别字段
class Module(models.Model):
"""
模块表
"""
level_choise = {
'1': '初级',
'2': '中级',
'3': '高级',
}
level = models.ImageField(verbose_name='级别',choices=level_choise,default=1)
name = models.CharField(verbose_name='模块名',max_length=32)
course = models.ForeignKey(verbose_name='课程', to='Course', on_delete=models.CASCADE)
class ModuleSerializer(serializers.ModelSerializer):
cname = serializers.CharField(source='course.title') # 定义连表需要显示的字段
level_test = serializers.CharField(source='get_level_display') # 定义本表需要显示的字段(以中文显示)
class Meta:
model = models.Module
fields = ['id', 'name', 'level_test', 'cname'] # 显示想要显示的字段
# 显示结果: [ { "id": 1, "name": "数据结构", "level_test": "初级", "cname": "Python" }, { "id": 2, "name": "正则表达式", "level_test": "初级", "cname": "Python" } ]
基于CreateAPIView,RetrieveAPIView等系列,实现对module表:单条数据展示、增加
urlpatterns = [
re_path('api/(?P<version>\w+)/module/new/$', views.ModuleNewView.as_view()), # 只对应POST方法
re_path('api/(?P<version>\w+)/module/new/(?P<pk>\d+)/$', views.ModuleNewView.as_view()), # 只对应GET方法
]
class ModuleNewSerializer(serializers.ModelSerializer):
class Meta:
model = models.Module
fields = "__all__"
from rest_framework.generics import ListAPIView, CreateAPIView,RetrieveAPIView
class ModuleNewView(CreateAPIView,RetrieveAPIView,):
queryset = models.Module.objects.all()
serializer_class = ModuleNewSerializer
基于ModelViewSet实现对module表实现增删改查:
urlpatterns = [
re_path('api/(?P<version>\w+)/module/set/$', views.ModuleSetView.as_view({'get':'list','post':'create'})),
re_path('api/(?P<version>\w+)/module/set/(?P<pk>\d+)/$', views.ModuleSetView.as_view({'get':'retrieve','delete':'destroy','patch':'partial_update','put':'update'})),
]
class ModuleSetSerializer(serializers.ModelSerializer):
class Meta:
model = models.Module
fields = "__all__"
from rest_framework.viewsets import ModelViewSet
class ModuleSetView(ModelViewSet):
queryset = models.Module.objects.all()
serializer_class = ModuleSetSerializer
# 重写partial_update方法实现部分更新,也可以不重写,因为源码里也是如此实现的
def partial_update(self, request, *args, **kwargs):
pk = kwargs.get('pk')
module_object = models.Module.objects.filter(id=pk).first()
ser = ModuleSetSerializer(instance=module_object,data=request.data,many=False,partial=True)
if ser.is_valid():
ser.save()
return Response('成功')
return Response(ser.errors)
对Video表做接口,获取视频列表、单条视频信息
urlpatterns = [
re_path('api/(?P<version>\w+)/video/$', views.VideoView.as_view()),
re_path('api/(?P<version>\w+)/video/(?P<pk>\d+)/$', views.VideoView.as_view()),
]
class VideoSerializer(serializers.ModelSerializer):
class Meta:
model = models.Video
fields = "__all__"
class VideoView(ListAPIView, CreateAPIView):
queryset = models.Video.objects.all()
serializer_class = VideoSerializer
对Video表做增删改查
# 这样定义可以省略{'get':'list','post':'create'...}
from django.urls import path, re_path, include
from rest_framework import routers
from api import views
route = routers.DefaultRouter()
route.register(r'video_set', views.VideoSetView)
urlpatterns = [
re_path('api/(?P<version>\w+)/',include(route.urls))
]
# 打印出多对多关联表的内容
class VideoSetSerializer(serializers.ModelSerializer):
tag_test = serializers.SerializerMethodField() # 多对多定制显示操作
class Meta:
model = models.Video
fields = ['id', 'title', 'vid', 'tag', 'tag_test']
# 钩子函数(get_+自定义字段),每次展示钩子函数都会执行一遍,
# obj表示每次展示每条数据对象
def get_tag_test(self, obj):
# 与之关联的tag表中的所有数据
tag_list = obj.tag.all()
return [{'id': row.id, 'caption': row.caption} for row in tag_list]
class VideoSetView(ModelViewSet):
queryset = models.Video.objects.all()
serializer_class = ser.VideoSetSerializer
1、免除csrftoken的认证
2、提供了一些视图类:APIView、ListAPIView、ModelViewSet,他的内部帮助我们写了get/post/delete...方法,帮助我们可以快速实现增删改查。
3、渲染器,页面的渲染。
4、序列化(表单验证+序列化)
5、解析器,解析request.body中的数据格式,并将其赋值到request.data中。
6、版本控制
1、HTTP请求方法有哪些?方法是干什么的
get/post/put/patch/delete
- 1
2、用drf做接口开发:数据显示
- 单表:最简单,定义files字段即可
- FK:基于source参数连表操作
- M2M:基于SerializerMethodField + 自定义一个钩子方法
- 注意:choise显示中文可以使用source字段
"get_字段名_display"
3、有三种写接口的方法
- APIView
- ListAPIView
- ModelViewSet
4、局部更新
局部更新应用用patch请求
- 1
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。