django restframework

本文详细介绍使用Django REST framework实现RESTful API的过程,包括自动化的API生成、浏览器页面展示、权限管理、接口操作及自定义功能,如返回字段、异常处理、权限、分页和复杂接口操作。

django restframework

前言

1、自动生成符合restful规范的API
2、自动为 API 生成浏览器页面
3、权限管理

本次演示

1 、restframework快速实现接口增删改查
2、统一自定义返回字段
3、统一异常处理
4、自定义权限
5、自定义分页
6、自定义复杂接口操作

开始

首先,
1、初始化框架,安装djangorestframework==3.6.3
2、创建app。python manage.py startapp rest
3、添加app到 INSTALLED_APPS
创建models

class TEST(models.Model):

    name = models.CharField(u'名称',max_length=50)
    time = models.DateTimeField(u'时间',default=datetime.datetime.now)

    class Meta:
        ordering = ('-time',)

序列化

serializer.py
class TestSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = TEST
        fields = ['id','name','time']
        #fields = '__all__'

set

views.py

from component.drf import viewsets as component_viewsets
class TestViewSet(component_viewsets.ModelViewSet):
    queryset = TEST.objects.all()
    serializer_class = TestSerializer

viewsets.py

from rest_framework import viewsets
from rest_framework.views import APIView
from mixins import ApiGenericMixin
class APIView(ApiGenericMixin, APIView):
    """APIView"""
    pass


class ModelViewSet(ApiGenericMixin, viewsets.ModelViewSet):
    """按需改造DRF默认的ModelViewSet类"""
    pass


class ReadOnlyModelViewSet(ApiGenericMixin, viewsets.ReadOnlyModelViewSet):
    """按需改造DRF默认的ModelViewSet类"""
    pass


class ViewSet(ApiGenericMixin, viewsets.ViewSet):
    """按需改造DRF默认的ViewSet类"""
    pass


class GenericViewSet(ApiGenericMixin, viewsets.GenericViewSet):
    """按需改造DRF默认的GenericViewSet类"""
    pass

统一结果返回

from rest_framework import status
from rest_framework.response import Response
from component.constants import ResponseCodeStatus


class ApiGenericMixin(object):
    """API视图类通用函数"""
    # TODO 权限部分加载基类中
    permission_classes = ()

    def finalize_response(self, request, response, *args, **kwargs):
        """统一数据返回格式"""
        if response.data is None:
            response.data = {
                'result': True,
                'code': ResponseCodeStatus.OK,
                'message': 'success',
                'data': []
            }
        elif isinstance(response.data, (list, tuple)):
            response.data = {
                'result': True,
                "code": ResponseCodeStatus.OK,
                "message": 'success',
                "data": response.data,
            }
        elif isinstance(response.data, dict) and "code" not in response.data:
            response.data = {
                'result': True,
                "code": ResponseCodeStatus.OK,
                "message": 'success',
                "data": response.data,
            }
        if response.status_code == status.HTTP_204_NO_CONTENT and request.method == "DELETE":
            response.status_code = status.HTTP_200_OK
            # response.status_text = "OK"

        return super(ApiGenericMixin, self).finalize_response(
            request, response, *args, **kwargs
        )

注册路由

urls.py
router = routers.DefaultRouter(trailing_slash=True)
router.register(r'test_a', views.TestViewSet,base_name='test')
urlpatterns = router.urls

ok,runserver,打开浏览器访问
在这里插入图片描述
打开postman,测试增删改查,成功!

扩展

统一异常处理是在settings里面配置

REST_FRAMEWORK = {
    'EXCEPTION_HANDLER': 'component.generics.exception_handler',
    'DEFAULT_PAGINATION_CLASS': 'component.drf.pagination.CustomPageNumberPagination',#自定义分页代码在下面扩展里面
    'PAGE_SIZE': 1,#每页数量
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.TokenAuthentication',
    ),
    'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',),
    'DATETIME_FORMAT': "%Y-%m-%d %H:%M:%S"
}

统一异常处理代码

from django.conf import settings
from rest_framework import status
from rest_framework.compat import set_rollback
from rest_framework.response import Response
from rest_framework.exceptions import (AuthenticationFailed,MethodNotAllowed,NotAuthenticated, PermissionDenied,ValidationError)
from django.http import Http404

from common.log import logger
from constants import ResponseCodeStatus
import traceback


def exception_handler(exc, context):
    """
        异常统一处理处理,拷贝自paas-ci项目
        分类:
            rest_framework框架内异常
            app自定义异常
    """
    data = {
        'result': False,
        'data': None
    }
    if isinstance(exc, (NotAuthenticated, AuthenticationFailed)):
        data = {
            'result': False,
            'code': ResponseCodeStatus.UNAUTHORIZED,
            'detail': u"用户未登录或登录态失效,请使用登录链接重新登录",
            'login_url': '',
        }
        return Response(data, status=status.HTTP_403_FORBIDDEN)

    if isinstance(exc, PermissionDenied):
        data = {
            'result': False,
            'code': ResponseCodeStatus.PERMISSION_DENIED,
            'message': exc.detail,
        }
        return Response(data, status=status.HTTP_403_FORBIDDEN)

    else:
        if isinstance(exc, ValidationError):
            data.update({
                'code': ResponseCodeStatus.VALIDATE_ERROR,
                'messages': exc.detail,
            })

        elif isinstance(exc, MethodNotAllowed):
            data.update({
                'code': ResponseCodeStatus.METHOD_NOT_ALLOWED,
                'message': exc.detail,
            })
        elif isinstance(exc, PermissionDenied):
            data.update({
                'code': ResponseCodeStatus.PERMISSION_DENIED,
                'message': exc.detail,
            })

        # elif isinstance(exc, ServerError):
        #     # 更改返回的状态为为自定义错误类型的状态码
        #     data.update({
        #         'code': exc.code,
        #         'message': exc.message,
        #     })
        elif isinstance(exc, Http404):
            # 更改返回的状态为为自定义错误类型的状态码
            data.update({
                'code': ResponseCodeStatus.OBJECT_NOT_EXIST,
                'message': u"当前操作的对象不存在",
            })
        else:
            # 调试模式
            logger.error(traceback.format_exc())
            print traceback.format_exc()
            if settings.RUN_MODE != 'PRODUCT':
                raise exc
            # 正式环境,屏蔽500
            data.update({
                'code': ResponseCodeStatus.SERVER_500_ERROR,
                'message': exc.message,
            })

        set_rollback()
        return Response(data, status=status.HTTP_200_OK)

分页

# -*- coding: utf-8 -*-
from collections import OrderedDict

from rest_framework.pagination import LimitOffsetPagination, PageNumberPagination
from rest_framework.response import Response


class CustomPageNumberPagination(PageNumberPagination):
    """
        自定义分页格式,综合页码和url
    """
    page_size_query_param = 'page_size'
    max_page_size = 10000

    def get_paginated_response(self, data):
        return Response(OrderedDict([
            ('page', self.page.number),
            ('total_page', self.page.paginator.num_pages),
            ('count', self.page.paginator.count),
            ('next', self.get_next_link()),
            ('previous', self.get_previous_link()),
            ('items', data)
        ]))

用到的转换函数

# -*- coding: utf-8 -*-

from collections import Counter, namedtuple

def tuple_choices(tupl):
    """从django-model的choices转换到namedtuple"""
    return [(t, t) for t in tupl]

def dict_to_namedtuple(dic):
    """从dict转换到namedtuple"""
    return namedtuple('AttrStore', dic.keys())(**dic)


def choices_to_namedtuple(choices):
    """从django-model的choices转换到namedtuple"""
    return dict_to_namedtuple(dict(choices))


def tuple_to_namedtuple(tupl):
    """从tuple转换到namedtuple"""
    return dict_to_namedtuple(dict(tuple_choices(tupl)))

constants.py

# -*- coding: utf-8 -*-

from django.utils.translation import ugettext as _

from .utils.basic import (choices_to_namedtuple, tuple_choices)

# 返回状态码
CODE_STATUS_TUPLE = (
    "OK",
    'UNAUTHORIZED',
    'VALIDATE_ERROR',
    'METHOD_NOT_ALLOWED',
    'PERMISSION_DENIED',
    "SERVER_500_ERROR",
    "OBJECT_NOT_EXIST"
)
CODE_STATUS_CHOICES = tuple_choices(CODE_STATUS_TUPLE)
ResponseCodeStatus = choices_to_namedtuple(CODE_STATUS_CHOICES)

复杂接口查询
views.py

from django.shortcuts import render
from rest.models import *
from rest_framework import viewsets
from serializer import TestSerializer
from component.drf import viewsets as component_viewsets
from rest.permissions import IsAdmin
from rest_framework.decorators import list_route,detail_route
from rest_framework.response import Response
"""
ModelViewSet 默认get获取所有数据,post添加数据,put更新,delete删除
ReadOnlyModelViewSet
"""
from django.views.decorators.csrf import csrf_exempt
class TestViewSet(component_viewsets.ModelViewSet):
    queryset = TEST.objects.all()
    serializer_class = TestSerializer
    #自定义权限
    permission_classes = (IsAdmin,)
    # 搜索和获取所有的
    @list_route(methods=["post"],url_path='list')
    def get_list(self, request, *args, **kwargs):
        keys = request.data.get('keys',None)
        records = self.queryset.filter(name__contains=keys)
        result=[]
        for item in records:
            result.append({
                'id':item.id,
                'title':item.name,
                'create_time':item.time
            })

        return Response(result)

    @list_route(methods=["post"],url_path='add')
    def add_obj(self, request, *args, **kwargs):

        return Response('POST请求,响应内容')

自定义权限
permissions.py

# -*- coding: utf-8 -*-

from rest_framework import permissions

class IsAdmin(permissions.BasePermission):
    message = u"对不起,您没有该操作的权限,请联系管理员。"

    def has_permission(self, request, view):

        if request.user.is_superuser:
            return True
        return False
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值