python全栈学习程序员

基于Django实现 RESTful API 之RestFram

2018-09-20  本文已影响190人  SlashBoyMr_wang

接下来学习RestFramework框架中的认证、权限、频率组件的使用

一、首先实现用户login登录认证功能

做用户登录认证功能可以通过session、cookie和token三种形式,下面的login认证基于token实现

附:models.py

from django.db import models

class Userinfo(models.Model):
    name = models.CharField(max_length=32)
    pwd = models.CharField(max_length=64)
    user_type = models.IntegerField(choices=((1,"common_user"),(2,"VIP_user"),(3,"SVIP_user")))

class UserToken(models.Model):
    user = models.OneToOneField(to="Userinfo")
    token = models.CharField(max_length=128)
from rest_framework.views import APIView
from rest_framework.response import Response
import uuid


class LoginView(APIView):
    """
    1000:成功
    1001:用户名或者密码错误
    1002:异常错误
    """
    def post(self, request):
        #自定义的返回体数据
        ret = {"code": 1000, "msg": None, "user": None}
        try:
            print(request.data)  # 重装后的request,request.data从中取所需的数据
            name = request.data.get("name")#获取前端用户名
            pwd = request.data.get("pwd")#获取前端密码
            #数据库校验用户名密码是否正确
            user_obj = models.Userinfo.objects.filter(name=name, pwd=pwd).first()
            if user_obj:
                #通过uuid模块获得随机字符串作为token值
                random_str = uuid.uuid4()
                #UserToken表中有该用户的信息就用新token值覆盖,没有就创建数据
                models.UserToken.objects.update_or_create(user=user_obj, defaults={"token": random_str})
                ret["user"] = user_obj.name
                ret["token"] = random_str
            else:
                ret["code"] = 1001
                ret["msg"] = "用户名或者密码错误"

        except Exception as e:
            ret["code"] = 1002
            ret["msg"] = str(e)

        return Response(ret)
这样就实现了用户的login认证。。。。。。。。。

二、基于RestFramework的认证组件做视图处理类的user认证

首先看一下UserAuth的具体用法:

这四点你一定看不懂是什么意思,很正常,这都是RestFramework源码给设定的特定规则,稍后认证源码流程分析会一一分析解答

view.py代码示例:代码实现了book视图类的用户认证功能

from rest_framework.exceptions import AuthenticationFailed
from rest_framework.authentication import BaseAuthentication

#用于认证的类
class UserAuth(BaseAuthentication):
   def authenticate(self, request):
       token = request.query_params.get("token")
       usertoken_obj = models.UserToken.objects.filter(token=token).first()
       if usertoken_obj:
           return usertoken_obj.user, usertoken_obj.token
       else:
           raise AuthenticationFailed("用户认证失败,您无权访问!!!")

# book视图处理类
class Booklist(ModelViewSet):
   authentication_classes = [UserAuth, ]

   queryset = models.Book.objects.all()
   serializer_class = BooklistSerializer

接下来一张图带你透析认证组件:

UserAuth认证源码解析.jpg

- 这样的写法只是给book视图类加上了认证,可以将认证类单独卸载一个py文件中,然后在全局settings中添加对应的配置项,这样就可以做到对所有的视图类添加认证功能了!!!

根据实际需求选择合适的方法!!!

app001-utils-Userauth.py

from app001 import models
from rest_framework.exceptions import AuthenticationFailed
from rest_framework.authentication import BaseAuthentication

class UserAuth(BaseAuthentication):
    def authenticate(self, request):
        token = request.query_params.get("token")
        usertoken_obj = models.UserToken.objects.filter(token=token).first()
        if usertoken_obj:
            return usertoken_obj.user, usertoken_obj.token
        else:
            raise AuthenticationFailed("用户认证失败,您无权访问!!!")

settings.py

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'app001.utils.Userauth.UserAuth',
    )
}

view.py

from app001 import models
from app001.serializers.bookserializer import BooklistSerializer

# 重装了APIView
from rest_framework.viewsets import ModelViewSet

# book视图处理类
class Booklist(ModelViewSet):
   queryset = models.Book.objects.all()
   serializer_class = BooklistSerializer

三、基于RestFramework的认证组件做视图处理类的权限认证

首先看一下UserAuth的具体用法:

permission源码执行流程:

permission源码解析_副本.jpg
from rest_framework.permissions import BasePermission
class SVIPPermission(BasePermission):
    message="您没有访问权限!"
    def has_permission(self,request,view):
        if request.user.user_type >= 2:
            return True
        return False
from app001 import models
from app001.serializers.bookserializer import BooklistSerializer
# 重装了APIView
from rest_framework.viewsets import ModelViewSet

from app001.utils.permission_class import SVIPPermission

# book视图处理类
class Booklist(ModelViewSet):
    permission_classes = [SVIPPermission,]

    queryset = models.Book.objects.all()
    serializer_class = BooklistSerializer

四、基于RestFramework的认证组件做视图处理类的访问频率限制

1、访问频率限制和认证、权限的执行流程一样,都是restframework源码提供的一种书写格式,再此就不一一赘述。

import time
from rest_framework.throttling import BaseThrottle

VISIT_RECORD = {}

class VisitThrottle(BaseThrottle):

    def __init__(self):
        self.history = None

    def allow_request(self, request, view):
        # 1. 拿到用户请求的IP
        # print(request.META)
        ip = request.META.get("REMOTE_ADDR")
        # 2. 当前请求的时间
        now = time.time()
        # 3. 记录访问的历史
        if ip not in VISIT_RECORD:
            VISIT_RECORD[ip] = []

        history = VISIT_RECORD[ip]
        self.history = history
        # [11:07:20, 10:07:11, 10:07:06, 10:07:01]
        while history and now - history[-1] > 60:
            history.pop()
        # 判断用户在一分钟的时间间隔内是否访问超过3次
        if len(history) >= 3:
            return False
        history.insert(0, now)

        return True

    def wait(self):
        # 当前访问时间
        ctime = time.time()
        #  访问时间历史记录 self.history
        return 60 - (ctime - self.history[-1])
from app001 import models
from app001.serializers.bookserializer import BooklistSerializer
# 重装了APIView
from rest_framework.viewsets import ModelViewSet
from app001.utils.throttle_classes import VisitThrottle

# book视图处理类
class Booklist(ModelViewSet):
    throttle_classes = [VisitThrottle, ]

    queryset = models.Book.objects.all()
    serializer_class = BooklistSerializer

2、基于RestFramework给我们提供了几种频率控制组件,省去了我们在自己写了

app001-utils-throttle_classes.py

from rest_framework.throttling import SimpleRateThrottle

class VisitThrottle(SimpleRateThrottle):
    scope="visit_rate"

    def get_cache_key(self,request, view):
        return self.get_ident(request)

settings.py

REST_FRAMEWORK = {
    "DEFAULT_THROTTLE_CLASSES": ("app001.utils.throttle_classes.VisitThrottle",),
    "DEFAULT_THROTTLE_RATES": {
        "visit_rate": "10/m",//频率设置
    },
}

view.py

from app001 import models
from app001.serializers.bookserializer import BooklistSerializer
# 重装了APIView
from rest_framework.viewsets import ModelViewSet
from app001.utils.throttle_classes import VisitThrottle

# book视图处理类
class Booklist(ModelViewSet):
    throttle_classes = [VisitThrottle, ]

    queryset = models.Book.objects.all()
    serializer_class = BooklistSerializer

五、基于RestFramework的视图处理类的分页组件

Restframework提供了几种很好的分页方式:

1、PageNumberPagination类的使用

from rest_framework.views import APIView
from app001 import models
# rest_framework重装的response
from rest_framework.response import Response
from app001.serializers.bookserializer import BooklistSerializer
from rest_framework.pagination import PageNumberPagination

class Booklist(APIView):

    def get(self, request):
        class MyPageNumberPagination(PageNumberPagination):
            page_ size = 2    //指定的每页显示数
            page_query_param = "page"      //url栏的页码参数
            page_size_query_param = "size"     //获取url参数中设置的每页显示数据条数
            max_page_size = 5       //最大支持的每页显示的数据条数

        book_obj = models.Book.objects.all()
        # 实例化
        pnp = MyPageNumberPagination()
        # 分页
        paged_book_list = pnp.paginate_queryset(book_obj, request)
        bs = BooklistSerializer(paged_book_list, many=True)
        data = bs.data  # 序列化接口
        return Response(data)
from rest_framework.viewsets import ModelViewSet
from rest_framework.pagination import PageNumberPagination
class MyPageNumberPagination(PageNumberPagination):
    page_size = 3

# book视图处理
class Booklist(ModelViewSet):
    pagination_class = MyPageNumberPagination

    queryset = models.Book.objects.all()
    serializer_class = BooklistSerializer

2、LimitOffsetPagination类的使用

from app001 import models
from app001.serializers.bookserializer import BooklistSerializer
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.pagination import LimitOffsetPagination

class MyPageNumberPagination(LimitOffsetPagination):
    max_limit = 3  # 最大限制默认是None
    default_limit = 2  # 设置每一页显示多少条
    limit_query_param = 'limit'  # 往后取几条
    offset_query_param = 'offset'  # 当前所在的位置

class Booklist(APIView):

    def get(self, request):
        book_obj = models.Book.objects.all()
        # 实例化
        pnp = MyPageNumberPagination()
        # 分页
        paged_book_list = pnp.paginate_queryset(book_obj, request)
        bs = BooklistSerializer(paged_book_list, many=True)
        data = bs.data  # 序列化接口
        # return Response(data) #不含上一页下一页
        return pnp.get_paginated_response(data)
- 了解LIMIT / OFFSET的实际工作原理。为此,我们举例说明,假如有大约1000个符合要求的结果。你若要前100数据,这很容易,这意味着它只返回与结果集匹配的前100行。但是现在想要900-1000之间的数据。数据库现在必须遍历前900个结果才能开始返回(因为它没有指针告诉它如何获得结果900)。总之,LIMIT / OFFSET在大型结果集上非常慢。

3、CursorPagination类的使用

-关于限制偏移量和游标分页的分析 请参考:http://cra.mr/2011/03/08/building-cursors-for-the-disqus-api

六、基于RestFramework的视图处理类响应器

REST_FRAMEWORK={
    'DEFAULT_RENDERER_CLASSES': (
        'rest_framework.renderers.JSONRenderer',  //响应json
        'rest_framework.renderers.BrowsableAPIRenderer',   //响应页面
    ),
}

七、基于RestFramework的视图处理类的url注册器

url注册器只是适用于视图类继承ModelViewSet类的写法:
url注册器会生成四条url:
^ ^booklist/$ [name='book-list']
^ ^booklist\.(?P<format>[a-z0-9]+)/?$ [name='book-list'] //可以指定数据类型
^ ^booklist/(?P<pk>[^/.]+)/$ [name='book-detail']
^ ^booklist/(?P<pk>[^/.]+)\.(?P<format>[a-z0-9]+)/?$ [name='book-detail'] //可以指定单个数据数据类型
urls.py

from rest_framework import routers
router = routers.DefaultRouter() //实例化
router.register("booklist", views.Booklist)   //注册

urlpatterns = [
    url(r'^', include(router.urls)),
]

view.py

from app001 import models
from app001.serializers.bookserializer import BooklistSerializer
# 重装了APIView
from rest_framework.viewsets import ModelViewSet

# book视图处理类
class Booklist(ModelViewSet):

    queryset = models.Book.objects.all()
    serializer_class = BooklistSerializer
上一篇下一篇

猜你喜欢

热点阅读