rest_framework视图和组件

一、视图

1.基本视图

#基本视图
#抽取基类
from rest_framework.response import Response
from rest_framework.views import APIView
from app1.models import *
from app1.Myserializer import BookSerializer
from app1.Myserializer import PublishSerializer
class List():
    def list(self,request,*args,**kwargs):
        response = {'status':100,'msg':'查询成功'}
        g = self.model
        g_ser = self.model_ser(instance=g,many=True)
        response['data'] = g_ser.data
        return Response(response)

class Create():
    def create(self,request,*args,**kwargs):
        response = {'status': 100, 'msg': '新增成功'}
        try:
            p_ser = self.model_ser(data=request.data)
            if p_ser.is_valid():
                p_ser.save()
                response['data'] = p_ser.data
            else:
                response['msg'] = p_ser.errors
        except Exception as e:
            response['msg'] = str(e)
        return Response(response)

#图书查询,新增
class BooksView(List,Create,APIView):
    model = Book.objects.all()
    model_ser = BookSerializer
    def get(self,request,*args,**kwargs):
        return self.list(request,*args,**kwargs)

    def post(self,request,*args,**kwargs):
        return self.create(request,*args,**kwargs)

#出版社查询,新增
class PublishView(List,Create,APIView):
    model = Publish.objects.all()
    model_ser = PublishSerializer
    def get(self,request,*args,**kwargs):
        return self.list(request,*args,**kwargs)

    def post(self,request,*args,**kwargs):
        return self.create(request,*args,**kwargs)

2.mixin类和generice类编写视图

from app1.models import *
from app1.Myserializer import BookSerializer
from rest_framework.mixins import ListModelMixin,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin
from rest_framework.generics import GenericAPIView
#ListModelMixin:获取所有 CreateModelMixin:新增 RetrieveModelMixin:获取单本 UpdateModelMixin:更新 DestroyModelMixin:删除
class BooksView(ListModelMixin,CreateModelMixin,GenericAPIView):
    serializer_class = BookSerializer
    queryset = Book.objects.all()
    #获取所有图书
    def get(self,request,*args,**kwargs):
        return self.list(request,*args,**kwargs)

    #新增图书
    def post(self,request,*args,**kwargs):
        return self.create(request,*args,**kwargs)

class BookView(RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin,GenericAPIView):
    serializer_class = BookSerializer
    queryset = Book.objects.all()

    #获取单本
    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)

3.使用generics 下ListCreateAPIView,RetrieveUpdateDestroyAPIView

from rest_framework.generics import ListCreateAPIView,RetrieveUpdateDestroyAPIView
#获取所有和新增图书
class BooksView(ListCreateAPIView):
    serializer_class = BookSerializer
    queryset = Book.objects.all()

#查询单个图书,更新,删除
class BookView(RetrieveUpdateDestroyAPIView):
    serializer_class = BookSerializer
    queryset = Book.objects.all()

4.使用ModelViewSet,将两个视图类写成一个类,五种请求:get,get,post,delete

from app1.models import *
from app1.Myserializer import BookSerializer
from rest_framework.viewsets import ModelViewSet
class BooksView(ModelViewSet):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

#路由层路由配置
url(r'^books/$', views.BooksView.as_view({'get':'list','post':'create'})),
url(r'^books/(?P<pk>\d+)$',\
    views.BooksView.as_view({'get':'retrieve','put':'update','delete':'destroy'})), 

5.ViewSetMixin的使用,重写了as_view方法

要用ViewSetMixin,路由跟之前写法不一样了
url(r'^test', views.Publish.as_view({'get':'aa'})),

视图类中
from rest_framework.viewsets import ViewSetMixin
class Publish(ViewSetMixin,APIView):
    def aa(self,request):
        return HttpResponse('aa')

二、认证组件

-什么是认证?是用来干什么的?
  校验用户是否是我的登录用户

-drf源码中找,认证如何实现的
-APIView的dispach方法---》self.initial(request, *args, **kwargs)----》400行self.perform_authentication(request)---》APIView的perform_authentication(request)----》request.user(request是新的request)---->去Request类中找user,执行--->self._authenticate()(self是新的reqeust对象)---->Request类的_authenticate(self)方法

使用认证功能

'''
1.①写一个认证类MyAuthentication,继承BaseAuthentication
②类的内部重写authenticate方法
③在authenticate方法中写认证逻辑,认证通过,返回None,认证不通过,抛异常
④校验通过,也可以返回当前登录的用户和auth,这样操作,在视图类中,可以通过request.user,获取到当前登录的用户
⑤一旦返回了值,后续的认证类就不会再走了
'''

'''
2.authentication_classes查找顺序(认证类的查找顺序): 
  ①先在自己类当中找:authentication_classes=[MyAuthentication]
  ②如果自己当中没有配置,去去项目settings.py中去找
  ③如果项目setting中没有配置,去drf的setting中找
'''

'''
3.最终在认证中的使用
    局部使用:
        -在视图类中配置:authentication_classes=[MyAuthentication]
    全局使用:
        -在setting.py中配置:
        REST_FRAMEWORK={
        'DEFAULT_AUTHENTICATION_CLASSES':['app1.MyAuth.MyAuthentication']
        }
    全局使用了局部禁用:
        -在视图类中配置:authentication_classes=[]
'''

from rest_framework.response import Response
from app1.models import *
from rest_framework.views import APIView
import uuid
from rest_framework.exceptions import AuthenticationFailed
from rest_framework.authentication import BaseAuthentication

class MyAuthentication(BaseAuthentication):
    def authenticate(self,request):
        #认证相关的东西
        #校验请求是否携带正确的token
        #取出token
        token = request.GET.get('token')
        #校验该次请求是否携带正确的token
        ret = UserToken.objects.filter(token=token).first()
        if ret:
            #正常通过认证的用户
            return ret.user,ret
        else:
            #没有登录或非法用户
            raise AuthenticationFailed('您没有通过认证')

class BooksView(APIView):
    #某个视图类需要登陆后才能查看,只需要在视图类加入下面代码
    authentication_classes=[MyAuthentication]
    def get(self,request):
        return Response('ok')



class Login(APIView):
    def post(self,request):
        response={'status':100,'msg':None}
        name = request.data.get('name')
        pwd = request.data.get('pwd')

        #去数据库校验数据是否存在
        user = User.objects.filter(name=name,pwd=pwd).first()
        if user:
            #正常用户登录成功

            #返回唯一的随机字符串
            token = uuid.uuid4()

            #生成随机的字符串存到数据库中,如果存在更新token,不存在新增一条
            # 根据user去查询,如果查到数据,更新defaults中的数据,如果查不到,新增一条数据
            ret = UserToken.objects.update_or_create(user=user,defaults={'token':token})
            response['msg'] = '登录成功'
            response['token'] = token
        else:
            response['status'] = 101
            response['msg'] = '账号或密码错误'
        return Response(response)
    
#请求 
#http://127.0.0.1:8000/books/?token=735061c3-733d-463d-8158-ccfa2e13f539 
#token匹配成功就会得到图书信息,否则返回没有通过认证

#加了 authentication_classes=[MyAuthentication] 这句代码,直接访问http://127.0.0.1:8000/books/ 会返回没有通过认证

三、权限组件

'''
权限是什么?
    比如只有超级用户可以访问books这个接口
使用:
    ①写一个权限类MyPermissions,继承BasePermission
    ②类的内部重写has_permission方法
    ③在has_permission方法内部写权限逻辑,通过返回True,失败返回False
'''
user表
class User(models.Model):
    name=models.CharField(max_length=32)
    pwd=models.CharField(max_length=32)
    choice=((1,'超级用户'),(2,'普通用户'),(3,'穷逼用户'))
    type = models.IntegerField(choices=choice,null=True)

from rest_framework.permissions import BasePermission
class MyPermissions(BasePermission):
    def has_permission(self, request, view):
        #是否为超级用户
        if request.user.type == 1:
            #如何取type对应的文字 get_字段名_display()
            user_type = request.user.get_type_display()
            #超级用户校验通过返回True
            return True
        else:
            #校验失败返回False
            return False
        
'''
最终的使用
局部使用:
    -在视图类中配置:permission_classes=[MyPermissions]
全局使用:
    -在settings.py中配置:
    REST_FRAMEWORK={
    'DEFAULT_PERMISSION_CLASSES':['app1.MyAuth.MyPermissions']
    }
全局使用了局部禁用:
    -在视图类中配置:permission_classes=[]

四、频率组件

'''
频率是什么?
 -同一段时间内,只能访问多少次
'''
频率组件的使用:
①写一个频率类Mythrottling,继承SimpleRetaThrottle
②重写get_cache_key方法,方法返回什么,频率组件就以什么做限制(比如返回ip,就以ip做限制.返回user_id,就会以用户id做限制)
from rest_framework.throttling import SimpleRateThrottle
class MyThrottling(SimpleRateThrottle):
    scope = 'xxx'
    def get_cache_key(self, request, view):
        return request.META.get('REMOTE_ADDR')  #返回客户端ip地址

③在setings.py中配置:
    'DEFAULT_THROTTLE_CLASSES':{
        'xxx:'3/m' #每分钟三次
    }

④局部使用:
    -在视图类中配置:throttle_classes=[MyThrottling]
 全局使用:
    -在settings.py中配置
    REST_FRAMEWORK={
    'DEFAULT_THROTTLE_CLASSES':['app1.MyAuth.MyThrottling']
    }
全局使用了局部禁用:
    -在视图类中配置:throttle_classes=[]

自定义频率控制类

'''
频率控制的逻辑
某个ip地址一分钟只能访问三次
{ip地址1:[第三次访问的时间,第二次访问的时间,第一次访问的时间],ip地址2:[]}
(1)取出访问者ip
(2)判断当前ip不在访问字典里,添加进去,并且直接返回True,表示第一次访问,在字典里,继续往下走
(3)循环判断当前ip的时间列表,有值,并且当前时间减去列表的最后一个时间大于60s,把列表中最后一个数用pop去掉,这样列表中只有60s以内的访问时间,
(4)判断,当列表小于3,说明一分钟以内访问不足三次,把当前时间插入到列表第一个位置,返回True,顺利通过
(5)当大于等于3,说明一分钟内访问超过三次,返回False验证失败
'''
from rest_framework.throttling import BaseThrottle
class MyThrottling(BaseThrottle):
    #访问者ip字典
    VISIT_RECORD = {}
    def __init__(self):
        #访问者ip对应的时间列表
        self.history=None
    def allow_request(self, request, view):
        #(1)取出访问者ip
        ip = request.META.get('REMOTE_ADDR')
        #获取当前时间
        import time
        ctime = time.time() 
        #(2)判断当前ip不在访问字典里,添加进去,并且直接返回True, 表示第一次访问,在字典里,继续往下走
        if ip not in self.VISIT_RECORD:
            self.VISIT_RECORD[ip]=[ctime,]
            return True
        #获取当前访问者的时间列表self.history
        self.history = self.VISIT_RECORD.get(ip)
        #(3)循环判断当前ip的时间列表,有值,并且当前时间减去列表的最后一个时间大于60s,把列表中最后一个数据用pop去掉,这样列表中只有60s以内的访问时间
        while self.history and ctime-self.history[-1] > 60:
            self.history.pop()
        #(4)判断,当列表小于3,说明一分钟以内访问不足三次,把当前时间插入到列表第一个位置,返回True,顺利通过
        #(5)当大于等于3,说明一分钟内访问超过三次,返回False验证失败

        if len(self.history) < 3:
            self.history.insert(0,ctime)
            return True
        else:
            return False
    
    #还要等待的时间
    def wait(self):
        import time
        ctime = time.time()
        return 60 - (ctime-self.history[-1])

 

posted @ 2019-04-11 15:11  Zhuang_Z  阅读(137)  评论(0编辑  收藏  举报