FBV 和 CBV
CBV 通过函数调用方法
FBV 通过类调用方法
其本质上都是 CBV
但是 FBV 内部封装了关于 method 的方法,由于基本上都是前端的请求,所有像GET,POST等方法用的频繁,
而CBV将这些方法封装了起来,使得开发时更便捷了许多,所以CBV更适合写接口
###### 标准的 ######
2.1 fbv方式请求的过程
用户发送url请求,Django会依次遍历路由映射表中的所有记录,一旦路由映射表其中的一条匹配成功了,
就执行视图函数中对应的函数名,这是fbv的执行流程
2.2 cbv方式请求的过程
当服务端使用cbv模式的时候,用户发给服务端的请求包含url和method,这两个信息都是字符串类型
服务端通过路由映射表匹配成功后会自动去找dispatch方法,然后Django会通过dispatch反射的方式找到类中对应的方法并执行
类中的方法执行完毕之后,会把客户端想要的数据返回给dispatch方法,由dispatch方法把数据返回经客户端
一:下载
pip install djangorestframewrok
二:Views
# restframework 常用的类 from rest_framework.views import APIView from rest_framework.generics import GenericAPIView from rest_framework.viewsets import GenericViewSet from rest_framework.viewsets import ModelViewSet from rest_framework.response import Response
写法1:
-- from rest_framework import serializers (1)和 models 表中的字段对应 (使用模块:serializers.Serializer) (2)全部字段直接写(使用模块:serializers.ModelSerializer) class Meta: model = models.Book fields = '__all__' (3)使用时需要调用 BookModelSerializers(queryset/data,many=True/False) 注:当需要返回一个 queryset 的时候必须加 many=True 这个参数,单条数据则不用 -- ----------Url---------- from app01 import views re_path('book/$',views.BookView.as_view()) ----------View---------- from rest_framework import serializers class BookModelSerializers(serializers.ModelSerializer): title = serializers.CharField() price = serializers.CharField() publish = serializers.CharField() from rest_framework.views import APIView from rest_framework.views import Response # 更新操作: def put(self,request,pk): shop = models.Shop.objects.filter(pk=pk).first() bs = ShopSerializers(shop,data=request.data) if bs.is_valid(): # 判断数据是否有误 print('bs: ',bs) bs.save() # 相当于调用了 updata方法 return Response(bs.data) # 返回 数据 else: return Response(bs.errors) # 添加操作: ps = PublishSerializers(data=request.data) if ps.is_valid(): ps.save() # create # 查询操作: class BookView(APIView): def get(self,request): book_list = models.Book.objects.all() serializers = BookModelSerializers(book_list,many=True) return Response(serializers.data) # 删除操作: def delete(self,request,pk): models.Shop.objects.filter(pk=pk).delete() return Response()
写法2:
-- (a) (1)mixins 模块中有 List,Create,Put...方法可以直接使用 (2)generics 主要作用还是调用 APIView 中的 -- ----------Url---------- from app01 import views re_path('book/$',views.BookView.as_view()) ----------View (a)---------- from rest_framework import mixins from rest_framework import generics from rest_framework import serializers from rest_framework import serializers class BookModelSerializers(serializers.ModelSerializer): title = serializers.CharField() price = serializers.CharField() publish = serializers.CharField() class AuthorView(mixins.ListModelMixin,generics.GenericAPIView): queryset = models.Author.objects.all() # 必须有的参数 :queryset serializer_class = AuthorModelSerializers # 必须有的参数 :serializer_class def get(self,request,*args,**kwargs): return self.list(request,*args,**kwargs) -- (b) (1)继承 generics.RetrieveUpdateDestroyAPIView 将上面的 mixins generics 全部封装成了一个模块 -- ----------View (b)---------- 比上面简单一点 class UserView(generics.RetrieveUpdateDestroyAPIView): queryset = models.User.objects.all() serializer_class = UserModelSerializers
写法三:
-- (1) as_view({参数}) 中传的参数是必须的,参数的名称还是固定的, 可根据需求来限定哪个url配定哪个参数 (2) view 中 的变量名也是固定的,不能改变, 当执行到某个父类的方法的时候,就会需要这两个参数 queryset -- queryset serializer_class -- ----------Url---------- from app01 import views re_path('book/$', views.Book.as_view({'get': 'list', 'post': 'create'})), re_path('book/(?P<pk>d+)/$', views.Book.as_view({'get': 'retrieve', 'put': 'update','delete':'destroy'})), ----------View---------- from rest_framework import viewsets class Book(viewsets.ModelViewSet): queryset = models.Book.objects.all() serializer_class = BookModelSerializers
四:认证组件
---------- View(局部认证) ---------- -- (1) BaseAuthentication 模块中 有俩个方法(authenticate , authenticate_header) 注:这俩个方法也是固定名字,两个必须写上,一个都不能少,否则报错 ! 模块中这两个方法都是返回空的,但是执行的时候当前函数就将 两个方法覆盖了,也就是少些一个 authenticate_header (2) 创建一个 返回 随机字符的 token (3) update_or_create -- 如果有就更新,如果没有就创建 -- # 认证 组件 from rest_framework import exceptions from rest_framework.authentication import BaseAuthentication from app01 import models class TokenAuth(BaseAuthentication): # 这个方法直接写在一个文件中 def authenticate(self,request): token = request.GET.get('token') # 获取 token token_obj = models.Token.objects.filter(token=token).first() # 验证 token 是否匹配 if not token_obj: raise exceptions.AuthenticationFailed('验证失败') else: return token_obj.user.name,token_obj.token # 返回一个 登陆校验判断 的随机字符串 def get_random_str(user): import hashlib,time ctime = str(time.time()) # 当前时间字符串 md5 = hashlib.md5(bytes(user,encoding='utf8')) # md5 加密,加盐 md5.update(bytes(ctime,encoding='utf8')) # 将字符串进行拼接 #md5.digest() 二进制 #md5.hexdigest() 十六进制 return md5.hexdigest() # 返回一个 十六进制 的加密随机字符 # 登陆验证 class LoginView(APIView): authentication_classes = [TokenAuth] def post(self,request): name = request.data.get('name') # 获取用户名 pwd = request.data.get('pwd') # 获取密码 user = models.User.objects.filter(name=name,pwd=pwd).first() # 数据库校验 res = {'code':0,'msg':None} if user: random_str = get_random_str(user.name) token = models.Token.objects.update_or_create(user=user,defaults={'token':random_str}) # 在数据库生成 token res['token'] = random_str res['code'] = 1 res['msg'] = '验证成功' else: res['msg'] = '验证失败' import json return Response(json.dumps(res)) ---------- View(全局认证) ---------- (1) settings 中配置 认证组件位置: # 全局认证组件 REST_FRAMEWORK = { "DEFAULT_AUTHENTICATION_CLASSES": ["app01.utils.TokenAuth",] } (2) 这样就不用写 authentication_classes = [TokenAuth] (3) 如果哪个方法不需要认证的话 只需要 authentication_classes = [] 将列表设为空
五:权限组件
# 权限组件 class SvipPermission(object): # 这个方法直接写在一个文件中 message = "只有超级用户才能访问" def has_permission(self,request,view): username = request.user user_type = models.User.objects.filter(name=username).first().user_type if user_type == 3: return True # 返回True 则验证通过 else: return False # 返回False 则验证不通过
# 只需类下面添加
permission_classes = [SvipPermission] # 权限组件 -- 局部
# 全局认证
REST_FRAMEWORK = {
"DEFAULT_PERMISSION_CLASSES":['app01.utils.SvipPermission'], # 全局权限组件
}
六:解析器 (用于将解析字符格式)
from rest_framework.parsers import JSONParser,FormParser parser_classes = [JSONParser, FormParser] # 局部 (用于将解析字符格式)
七:频率组件:
- 自定义:
# 定义一个频率认证类 - 之后在视图配置 import time from rest_framework.throttling import BaseThrottle class MyThrottle(BaseThrottle): visited_record = {} def __init__(self): self.history = None def allow_request(self, request, my_cbv): # 这个my_cbv是源码中传的我们的视图类,这里我们也要传进去 # print(self.get_ident(request)) # 可以获取本次请求的ip ip = request.META.get("REMOTE_ADDR") if ip not in self.visited_record: self.visited_record[ip] = [] current_time = time.time() history = self.visited_record[ip] self.history = history print(history) while history and current_time - history[-1] > 60: # 把大于60秒的时间都删掉 history.pop() if len(history) > 2: # 第三次访问,列表中只有2个值,也满足条件 return False history.insert(0, current_time) return True def wait(self): """ 用于返回还剩多少时间访问; 本次访问时间:9:50:55 [09:50:30, 09:50:20, 09:50:10] 剩余 60 - (9:50:55 - 09:50:10)秒才能访问 :return: """ c_time = time.time() return 60 - (c_time - self.history[-1]) #### 局部使用 #### from rest_framework import generics from rest_framework import mixins from app01.utils.frequency import MyThrottle class ListView(generics.GenericAPIView, mixins.ListModelMixin, mixins.CreateModelMixin): throttle_classes = [MyThrottle] def get(self, request, *args, **kwargs): return Response('ok') #### 全局使用 #### REST_FRAMEWORK = { 'DEFAULT_PARSER_CLASSES': ( 'rest_framework.parsers.JSONParser', 'rest_framework.parsers.FormParser', 'rest_framework.parsers.MultiPartParser' ), 'DEFAULT_THROTTLE_CLASSES': ( 'app01.utils.frequency.MyThrottle', ), }
- 内置
#### 定义类 #### from rest_framework.throttling import SimpleRateThrottle class MyThrottle(SimpleRateThrottle): scope = "visit_rate" # 这个值决定了在配置时使用那个变量描述限制的频率 def get_cache_key(self, request, view): # 这个方法也是必须要有 return self.get_ident(request) #### 只能在全局使用 #### REST_FRAMEWORK = { 'DEFAULT_THROTTLE_CLASSES': ( 'app01.utils.throttle_class.MyThrottle', ), "DEFAULT_THROTTLE_RATES": { "visit_rate": "10/m", # 这个参数就是频率类中定义的那个参数scope, 其中第一个数字10表示10次,后面的m表示一分钟,还有s,一秒, h, 一小时, d, 一天 } }
八:分页器 -- 用于 restframework 内置的调试页面
# 要使用 restframework 自带的调试数据网页 需要在 settings 中配置
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app01.apps.App01Config',
'rest_framework', # 配置 restframework 组件
]
# 分页器 from rest_framework.pagination import PageNumberPagination class BookPageNumberPagination(PageNumberPagination): page_size = 1 # 每页显示个数 page_query_param = 'page' page_size_query_param = 'size' # 修改当前页显示个数 #max_page_size = 2 # 每页显示个数限制 class BookView(APIView): def get(self,request): book_list = models.Book.objects.all() # 分页器 pnp = BookPageNumberPagination() book_page = pnp.paginate_queryset(book_list,request,self) bs = BookModelSerializers(book_page,many=True,context={'request': request}) return Response(bs.data)