Django REST framework 2

Authenticate

  • Authentication is a set of identification credentials (e.g., request from a user, or the signature of the token) mechanism associated with the incoming request. Then the rights and restrictions assembly decide whether to reject the request.
  • Certification determine who you are
  • Permissions determine if you can access an interface
  • Make sure you restrict access to a frequency interface

A custom Token Authentication

REST framework provides a number of authentication schemes, and also allows custom programs.

1, Table:

class UserInfo(models.Model):
    username = models.CharField(max_length=16)
    password = models.CharField(max_length=32)
    type = models.SmallIntegerField(
        choices=((1, '普通用户'), (2, 'VIP用户')),
        default=1
    )


class Token(models.Model):
    user = models.OneToOneField(to='UserInfo')
    token_code = models.CharField(max_length=128)
 

2、定义一个登录视图:

def get_random_token(username):
    """
    根据用户名和时间戳生成随机token
    :param username:
    :return:
    """
    import hashlib, time
    timestamp = str(time.time())
    m = hashlib.md5(bytes(username, encoding="utf8"))
    m.update(bytes(timestamp, encoding="utf8"))
    return m.hexdigest()


class LoginView(APIView):
    """
    校验用户名密码是否正确从而生成token的视图
    """
    def post(self, request):
        res = {"code": 0}
        print(request.data)
        username = request.data.get("username")
        password = request.data.get("password")

        user = models.UserInfo.objects.filter(username=username, password=password).first()
        if user:
            # 如果用户名密码正确
            token = get_random_token(username)
            models.Token.objects.update_or_create(defaults={"token_code": token}, user=user)
            res["token"] = token
        else:
            res["code"] = 1
            res["error"] = "用户名或密码错误"
        return Response(res)

3、定义一个认证类

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


class MyAuth(BaseAuthentication):
    def authenticate(self, request):
        if request.method in ["POST", "PUT", "DELETE"]:
            request_token = request.data.get("token", None)
            if not request_token:
                raise AuthenticationFailed('缺少token')
            token_obj = models.Token.objects.filter(token_code=request_token).first()
            if not token_obj:
                raise AuthenticationFailed('无效的token')
            return token_obj.user.username, None
        else:
            return None, None

4、视图级别认证

class CommentViewSet(ModelViewSet):

    queryset = models.Comment.objects.all()
    serializer_class = app01_serializers.CommentSerializer
    authentication_classes = [MyAuth, ]

5、全局级别认证

# 在settings.py中配置
REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": ["app01.utils.MyAuth", ]
}

权限

只有VIP用户才能看的内容。

一、自定义

1、自定义一个权限类

# 自定义权限
class MyPermission(BasePermission):
    message = 'VIP用户才能访问'

    def has_permission(self, request, view):
        """
        自定义权限只有VIP用户才能访问
        """
        # 因为在进行权限判断之前已经做了认证判断,所以这里可以直接拿到request.user
        if request.user and request.user.type == 2:  # 如果是VIP用户
            return True
        else:
            return False

2、视图级别配置

class CommentViewSet(ModelViewSet):

    queryset = models.Comment.objects.all()
    serializer_class = app01_serializers.CommentSerializer
    authentication_classes = [MyAuth, ]
    permission_classes = [MyPermission, ]

3、全局级别设置

# 在settings.py中设置rest framework相关配置项
REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": ["app01.utils.MyAuth", ],
    "DEFAULT_PERMISSION_CLASSES": ["app01.utils.MyPermission", ]
}

限制

DRF内置了基本的限制类,首先我们自己动手写一个限制类,熟悉下限制组件的执行过程。

一、自定义

1、自定义限制类

VISIT_RECORD = {}
# 自定义限制
class MyThrottle(object):

    def __init__(self):
        self.history = None

    def allow_request(self, request, view):
        """
        自定义频率限制60秒内只能访问三次
        """
        # 获取用户IP
        ip = request.META.get("REMOTE_ADDR")
        timestamp = time.time()
        if ip not in VISIT_RECORD:
            VISIT_RECORD[ip] = [timestamp, ]
            return True
        history = VISIT_RECORD[ip]
        self.history = history
        history.insert(0, timestamp)
        while history and history[-1] < timestamp - 60:
            history.pop()
        if len(history) > 3:
            return False
        else:
            return True

    def wait(self):
        """
        限制时间还剩多少
        """
        timestamp = time.time()
        return 60 - (timestamp - self.history[-1])

2、视图使用

class CommentViewSet(ModelViewSet):

    queryset = models.Comment.objects.all()
    serializer_class = app01_serializers.CommentSerializer
    throttle_classes = [MyThrottle, ]

3、全局使用

# 在settings.py中设置rest framework相关配置项
REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": ["app01.utils.MyAuth", ],
    "DEFAULT_PERMISSION_CLASSES": ["app01.utils.MyPermission", ]
    "DEFAULT_THROTTLE_CLASSES": ["app01.utils.MyThrottle", ]
}

一、内置

1、使用内置限制类

from rest_framework.throttling import SimpleRateThrottle


class VisitThrottle(SimpleRateThrottle):

    scope = "xxx"

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

2、全局配置

# 在settings.py中设置rest framework相关配置项
REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": ["app01.utils.MyAuth", ],
    # "DEFAULT_PERMISSION_CLASSES": ["app01.utils.MyPermission", ]
    "DEFAULT_THROTTLE_CLASSES": ["app01.utils.VisitThrottle", ],
    "DEFAULT_THROTTLE_RATES": {
        "xxx": "5/m",
    }
}

DRF内置分页器

  • rest framework中提供了三种分页模式:
  • from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination, CursorPagination

一、PageNumberPagination

  • 按页码数分页,第n页,每页显示m条数据
  • 例如:http://127.0.0.1:8000/api/article/?page=2&size=1

1、分页器

class MyPageNumber(PageNumberPagination):
    page_size = 2  # 每页显示多少条
    page_size_query_param = 'size'  # URL中每页显示条数的参数
    page_query_param = 'page'  # URL中页码的参数
    max_page_size = None  # 最大页码数限制

2、视图

class ArticleList(APIView):
    def get(self, request, *args, **kwargs):
        res = {"code": 0}
        article_list = models.Article.objects.all().order_by("id")
        # 分页
        page_obj = MyPageNumber()
        page_article = page_obj.paginate_queryset(queryset=article_list, request=request, view=self)
        ser_obj = ArticleSerializer(page_article, many=True)
        res["data"] = ser_obj.data
        return Response(res)

3、返回带页码链接的响应

class ArticleList(APIView):
    def get(self, request, *args, **kwargs):
        res = {"code": 0}
        article_list = models.Article.objects.all().order_by("id")
        # 分页
        page_obj = MyPageNumber()
        page_article = page_obj.paginate_queryset(queryset=article_list, request=request, view=self)
        ser_obj = ArticleSerializer(page_article, many=True)
        res["data"] = ser_obj.data
        return page_obj.get_paginated_response(res)

二、LimitOffsetPagination 

  • 分页,在n位置,向后查看m条数据
  • 例如:http://127.0.0.1:8000/api/article/?offset=2&limit=2

1、分页器

# offset分页
class MyLimitOffset(LimitOffsetPagination):
    default_limit = 1
    limit_query_param = 'limit'
    offset_query_param = 'offset'
    max_limit = 999

2、视图

class ArticleList(APIView):
    def get(self, request, *args, **kwargs):
        res = {"code": 0}
        article_list = models.Article.objects.all().order_by("id")
        # 分页
        page_obj = MyLimitOffset()
        page_article = page_obj.paginate_queryset(queryset=article_list, request=request, view=self)
        ser_obj = ArticleSerializer(page_article, many=True)
        res["data"] = ser_obj.data
        return page_obj.get_paginated_response(res)

三、CursorPagination

  • 加密分页,把上一页和下一页的id值记住

1、分页器

# 加密分页
class MyCursorPagination(CursorPagination):
    cursor_query_param = 'cursor'
    page_size = 1
    ordering = '-id'  # 重写要排序的字段

2、视图

class ArticleList(APIView):
    def get(self, request, *args, **kwargs):
        res = {"code": 0}
        article_list = models.Article.objects.all().order_by("id")
        # 分页
        page_obj = MyCursorPagination()
        page_article = page_obj.paginate_queryset(queryset=article_list, request=request, view=self)
        ser_obj = ArticleSerializer(page_article, many=True)
        res["data"] = ser_obj.data
        # return Response(res)
        return page_obj.get_paginated_response(res)

四、全局配置

REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
    'PAGE_SIZE': 100
}

五、局部配置

可以在视图类中进行局部设置

class PublisherViewSet(ModelViewSet):
queryset = models.Publisher.objects.all()
serializer_class = PublisherModelSerializer
pagination_class = PageNumberPagination # 注意不是列表(只能有一个分页模式)

解析器

  • 解析器的作用就是服务端接收客户端传过来的数据,把数据解析成自己可以处理的数据。本质就是对请求体中的数据进行解析。
  • 在了解解析器之前,要先知道Accept以及ContentType请求头。
  • Accept是告诉对方能解析什么样的数据,通常也可以表示想要什么样的数据。
  • 解析器工作原理的就是拿到请求的ContentType来判断前端给后端的数据类型是什么,然后在后端使用相应的解析器去解析数据。

一、Django中的数据解析

  • 在视图中可以通过request.POST来获取前端发来的请求数据,那么Django框架是如何拿到请求体中的数据的呢?
  • 首先,request对象是 WSGIRequest 类的实例化对象
  • 在Django的视图中通过request.POST和request.FILES能够取到数据都是因为在这里把请求的数据解析,并赋值给request对象了。
  • Django的解析器是不支持 ContenType为 application/json 的,也就是说无法解析json格式的数据。

二、DRF中的解析器

  • DRF中获取请求提交的数据是通过访问request.data
  • 如果没有配置解析器,DRF会使用默认的解析器:
  • 可以在单个视图或者全局的settings.py中配置要使用的解析器。
  • 注意:当项目中只配置了 JSONParser 解析器时,就只能解析JSON格式的数据了,客户端如果使用浏览器提交,将无法解析。
  • 注意,在视图类中定义的配置项的优先级要高于全局配置中的配置项。

1、单个视图配置

class BookViewSet(ModelViewSet):
    queryset = models.Book.objects.all()
    serializer_class = BookModelSerializer
    parser_classes = [JSONParser, ]

2、全局配置

REST_FRAMEWORK = {
    'DEFAULT_PARSER_CLASSES': (
        'rest_framework.parsers.JSONParser',
    )
}

渲染器

  • 渲染器同解析器相反,它定义了框架按照content_type来返回不同的响应。
  • 可以在视图中局部设置也可以在全局的settings.py中进行设置
  • 注意,在视图类中定义的配置项的优先级要高于全局配置中的配置项。
  • DRF提供的渲染器有很多,默认是:
 'DEFAULT_RENDERER_CLASSES': (
        'rest_framework.renderers.JSONRenderer',
        'rest_framework.renderers.BrowsableAPIRenderer',
    ),

1、局部设置

这样设置后就只能返回JSON格式的数据了,并不会像之前一样提供一个阅读友好的web页面。

class PublisherViewSet(ModelViewSet):
    queryset = models.Publisher.objects.all()
    serializer_class = PublisherModelSerializer
    renderer_classes = [JSONRenderer, ]

2、全局设置

REST_FRAMEWORK = {
    'DEFAULT_RENDERER_CLASSES': (
        'rest_framework.renderers.JSONRenderer',
    ),
}

DRF的版本控制

  • API 版本控制允许在不同的客户端之间更改行为(同一个接口的不同版本会返回不同的数据)。 DRF提供了许多不同的版本控制方案。
  • 可能会有一些客户端因为某些原因不再维护了,但是后端的接口还要不断的更新迭代,这个时候通过版本控制返回不同的内容就是一种不错的解决方案。

一、DRF提供的版本控制方案

DRF提供了五种版本控制方案,如下图:

二、版本控制系统的使用

1、全局配置

这里以URLPathVersioning 为例,还是在项目的settings.py中REST_FRAMEWORK配置项下配置:

REST_FRAMEWORK = {
    ...
    'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning',
    'DEFAULT_VERSION': 'v1',  # 默认的版本
    'ALLOWED_VERSIONS': ['v1', 'v2'],  # 有效的版本
    'VERSION_PARAM': 'version',  # 版本的参数名与URL conf中一致
}

2、urls.py中

urlpatterns = [
    ...
    url(r'^(?P<version>[v1|v2]+)/publishers/$', views.PublisherViewSet.as_view({'get': 'list', 'post': 'create'})),
    url(r'^(?P<version>[v1|v2]+)/publishers/(?P<pk>\d+)/$', views.PublisherViewSet.as_view({'get': 'retrieve', 'put': 'update', 'delete': 'destroy'})),

]

3、视图中

  • 视图中可以通过访问 request.version 来获取当前请求的具体版本,然后根据不同的版本来返回不同的内容。
  • 可以在视图中自定义具体的行为,下面以不同的版本返回不同的序列化类为例。
class PublisherViewSet(ModelViewSet):

    def get_serializer_class(self):
        """不同的版本使用不同的序列化类"""
        if self.request.version == 'v1':
            return PublisherModelSerializerVersion1
        else:
            return PublisherModelSerializer
    queryset = models.Publisher.objects.all()

4、局部配置

注意,通常是不会单独给某个视图设置版本控制的,如果你确实需要给单独的视图设置版本控制,可以在视图中设置versioning_class属性,如下:

class PublisherViewSet(ModelViewSet):

    ...
    versioning_class = URLPathVersioning

Guess you like

Origin www.cnblogs.com/bubu99/p/11112008.html