• python


    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)
  • 相关阅读:
    Spring中的AOP(五)——定义切入点和切入点指示符
    Java获取URL对应的资源
    最简单的视频网站(JavaEE+FFmpeg)
    Android 为应用添加数字角标
    ViewTreeObserver简介
    Android中pendingIntent的深入理解
    在Windows的CMD中如何设置支持UTF8编码?
    android中角标的实现
    ANDROID SHAPE画圆形背景_ANDROID实现角标布局
    Android 学习之 开源项目PullToRefresh的使用
  • 原文地址:https://www.cnblogs.com/chaoqi/p/10446586.html
Copyright © 2020-2023  润新知