Django rest framework

 

 

    1. 开发模式
        - 普通开发方式(前后端放在一起写)
        - 前后端分离
    
    2. 后端开发
        为前端提供URL(API/接口的开发)
        注:永远返回HttpResponse

 

Django的FBV和CBV

 

FBV :function base view

def users(request):
    user_list = ['alex','oldboy']
    return HttpResponse(json.dumps((user_list)),status=200) #状态码

 

CBV:class base view

路由:
    url(r'^students/', views.StudentsView.as_view()),
试图:
    #根据请求方式不同自动调用相应的方法
    from django.views import View

    class StudentsView(View):

     def get(self,request,*args,**kwargs):
         return HttpResponse('GET',status=200)

    def post(self, request, *args, **kwargs):
        return HttpResponse('POST',status=200)

    def put(self, request, *args, **kwargs):
        return HttpResponse('PUT',status=200)

    def delete(self, request, *args, **kwargs):
        return HttpResponse('DELETE',status=200)    

 

原理:

CBV,基于反射实现根据请求方式不同,执行不同的方法。

url -> view方法 -> dispatch方法(反射执行其他:GET/POST/DELETE/PUT)

流程:

流程:
    class StudentsView(View):
        def dispatch(self, request, *args, **kwargs):
            print('before')
            ret = super(StudentsView,self).dispatch(request, *args, **kwargs)
            print('after')
            return ret

        def get(self,request,*args,**kwargs):
            return HttpResponse('GET')

        def post(self, request, *args, **kwargs):
            return HttpResponse('POST')

        def put(self, request, *args, **kwargs):
            return HttpResponse('PUT')

        def delete(self, request, *args, **kwargs):
            return HttpResponse('DELETE')

继承:多个类共用的功能,为了避免重复编写

继承(多个类共用的功能,为了避免重复编写):
    from django.views import View


    class MyBaseView(object):
        def dispatch(self, request, *args, **kwargs):
            print('before')
            ret = super(MyBaseView,self).dispatch(request, *args, **kwargs)
            print('after')
            return ret

    class StudentsView(MyBaseView,View):

        def get(self,request,*args,**kwargs):
            print('get方法')
            return HttpResponse('GET')

        def post(self, request, *args, **kwargs):
            return HttpResponse('POST')

        def put(self, request, *args, **kwargs):
            return HttpResponse('PUT')

        def delete(self, request, *args, **kwargs):
            return HttpResponse('DELETE')

    class TeachersView(MyBaseView,View):

        def get(self,request,*args,**kwargs):
            return HttpResponse('GET')

        def post(self, request, *args, **kwargs):
            return HttpResponse('POST')

        def put(self, request, *args, **kwargs):
            return HttpResponse('PUT')

        def delete(self, request, *args, **kwargs):
            return HttpResponse('DELETE')

  

什么是RESTful

REST与技术无关,代表的是一种软件架构风格,REST是Representational State Transfer的简称,中文翻译为“表征状态转移”

REST从资源的角度类审视整个网络,它将分布在网络中某个节点的资源通过URL进行标识,客户端应用通过URL来获取资源的表征,获得这些表征致使这些应用转变状态

所有的数据,不过是通过网络获取的还是操作(增删改查)的数据,都是资源,将一切数据视为资源是REST区别与其他架构风格的最本质属性

对于REST这种面向资源的架构风格,有人提出一种全新的结构理念,即:面向资源架构(ROA:Resource Oriented Architecture)

  

restful API设计  规范

1. API与用户的通信协议,总是使用HTTPs协议。

2.域名 
    https://api.example.com           尽量将API部署在专用域名(会存在跨域问题)
    https://example.org/api/                API很简单

3.版本
    URL,如:https://api.example.com/v1/
    请求头                                        跨域时,引发发送多次请求

4. 路径,视网络上任何东西都是资源,均使用名词表示(可复数)
    https://api.example.com/v1/zoos
    https://api.example.com/v1/animals
    https://api.example.com/v1/employees

5.method
    GET      :从服务器取出资源(一项或多项)
    POST    :在服务器新建一个资源
    PUT      :在服务器更新资源(客户端提供改变后的完整资源)
    PATCH  :在服务器更新资源(客户端提供改变的属性)
    DELETE :从服务器删除资源

6.过滤,通过在url上传参的形式传递搜索条件
https://api.example.com/v1/zoos?limit=10:指定返回记录的数量
https://api.example.com/v1/zoos?offset=10:指定返回记录的开始位置

7.状态码

8.错误处理,状态码是4xx时,应返回错误信息,error当做key
    {
        error: "Invalid API key"
    }

9.返回结果,针对不同操作,服务器向用户返回的结果应该符合以下规范。
    GET /collection:返回资源对象的列表(数组)
    GET /collection/resource:返回单个资源对象
    POST /collection:返回新生成的资源对象
    PUT /collection/resource:返回完整的资源对象
    PATCH /collection/resource:返回完整的资源对象
    DELETE /collection/resource:返回一个空文档

10. Hypermedia API,RESTful API最好做到Hypermedia,即返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么。
    {"link": {
      "rel":   "collection https://www.example.com/zoos",
      "href":  "https://api.example.com/zoos",
      "title": "List of zoos",
      "type":  "application/vnd.yourformat+json"
    }}

  

csrf装饰器的使用

from django.views.decorators.csrf import csrf_exempt,csrf_protect

# @csrf_protect #需要验证
# @csrf_exempt  #不需要验证


# 给CBV加装饰器
from django.views import View
from django.utils.decorators import method_decorator

# 方式一:使用method_decorator
class StudentsView(View):

    @method_decorator(csrf_exempt)
    def dispatch(self, request, *args, **kwargs):
        return super(StudentsView, self).dispatch(request, *args, **kwargs)

    def get(self, request, *args, **kwargs):
        print('get方法')
        return HttpResponse('GET')

    def post(self, request, *args, **kwargs):
        return HttpResponse('POST')

    def put(self, request, *args, **kwargs):
        return HttpResponse('PUT')

    def delete(self, request, *args, **kwargs):
        return HttpResponse('DELETE')

#方式二:给类加装饰器,name标明要装饰的函数

@method_decorator(csrf_exempt,name='dispatch')
class ClassView(View):

    @method_decorator(csrf_exempt)
    def dispatch(self, request, *args, **kwargs):
        return super(ClassView, self).dispatch(request, *args, **kwargs)

    def get(self, request, *args, **kwargs):
        print('get方法')
        return HttpResponse('GET')

    def post(self, request, *args, **kwargs):
        return HttpResponse('POST')

    def put(self, request, *args, **kwargs):
        return HttpResponse('PUT')

    def delete(self, request, *args, **kwargs):
        return HttpResponse('DELETE')

 

基于Django Rest Framework框架实现

 

1. 认证和授权

 

a. 用户url传入的token认证

from django.conf.urls import url, include
from web.viewsimport TestView

urlpatterns = [
    url(r'^test/', TestView.as_view()),
]
urls.py
from django.shortcuts import render,HttpResponse
from django.views import View
from rest_framework.views import APIView
from rest_framework.request import Request
from rest_framework.authentication import BasicAuthentication
from rest_framework import exceptions
from app01.models import *

class MyAuthentication(object):
    """
        用户认证,如果验证成功后返回元组: (用户,用户Token)
        :param request: 加工后的request
        :return: (user,auth)
            None,表示跳过该验证;
                如果跳过了所有认证,默认用户和Token和使用配置文件进行设置
                self._authenticator = None
                if api_settings.UNAUTHENTICATED_USER:
                    self.user = api_settings.UNAUTHENTICATED_USER()
                else:
                    self.user = None
        
                if api_settings.UNAUTHENTICATED_TOKEN:
                    self.auth = api_settings.UNAUTHENTICATED_TOKEN()
                else:
                    self.auth = None
            (user,token)表示验证通过并设置用户名和Token;
            AuthenticationFailed异常
    """
    def authenticate(self,request):
        # request是加工过后对request,request._request是原来对request
        username = request._request.GET.get('username')
        password = request._request.GET.get('password')
        print(username,password)
        user = UserInfo.objects.filter(username=username,password=password).first()
        if not user:
            raise exceptions.AuthenticationFailed("验证失败")
        return (user,'auth')  #self.user, self.auth = user_auth_tuple
    def authenticate_header(self,val):
        pass


class StudentsView(APIView):
    authentication_classes = [MyAuthentication,]


    def get(self, request, *args, **kwargs):
        # self.dispatch(request, *args, **kwargs)
        print(request.authenticators,"对象列表")
        #[<app01.views.MyAuthentication object at 0x105ca9b38>] 对象列表
        print(request._authenticator,"对象")
        #<app01.views.MyAuthentication object at 0x105ca9b38> 对象

        print(request.user.username)  #zhou
        print(request.auth)     #auth
        return HttpResponse("ok")

    def post(self, request, *args, **kwargs):
        return HttpResponse('POST')

    def put(self, request, *args, **kwargs):
        return HttpResponse('PUT')

    def delete(self, request, *args, **kwargs):
        return HttpResponse('DELETE')
views.py

b. 登陆后生成token保存到数据库,访问时根据URL传入的token认证

from django.conf.urls import url
from django.contrib import admin
from app01 import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^api/v1/auth', views.Authview.as_view()),
    url(r'^api/v1/order', views.Orderview.as_view()),

]
urls.py
from django.shortcuts import render,HttpResponse,render
from rest_framework.views import APIView
from rest_framework.request import Request
from rest_framework import exceptions
from rest_framework.authentication import BaseAuthentication
from app01.models import *
from django.http import JsonResponse


def md5(user):
    """
    制作token

    """
    import hashlib
    import time
    stime = str(time.time())
    m = hashlib.md5(bytes(user,encoding="utf-8"))
    m.update(bytes(stime,encoding="utf-8"))
    return m.hexdigest()


class Authview(APIView):
    """
    登陆校验,登陆成功后生成一个token存到数据库
    """

    def post(self,request):
        ret = {"code":1000,"msg":None}
        username = request._request.POST.get("username")
        pwd = request._request.POST.get("pwd")
        try:
            user = UserInfo.objects.filter(username=username,password=pwd).first()
            if not user:
                ret["code"] = 1001
                ret["msg"] = "用户名或密码错误"
            else:
                token = md5(username)
                #有则更新,没有则创建
                UserToken.objects.update_or_create(user=user,defaults={"token":token})
                ret["token"] = token
        except Exception as e:
            ret['code'] = 1002
            ret['msg'] = '请求出现错误:%s'%e


        return JsonResponse(ret)


class Myauthenticat(BaseAuthentication):
    """
    自定义认证类,必须实现这两个方法
    """
    def authenticate(self,request):
        token = request._request.GET.get("token")
        if not token:
            raise exceptions.AuthenticationFailed("认证失败")
        obj = UserToken.objects.filter(token=token).first()
        if not obj:
            raise exceptions.AuthenticationFailed("验证码错误")
        return (obj.user,obj.token)

    def authenticate_header(self,val):
        pass


class Orderview(APIView):
    # 认证
    authentication_classes=[Myauthenticat,]

    def get(self,request):

        print(request.user,request.auth) # UserInfo object ,b5c552a6c744818c8fe10d7f8554a4c6
        res = {"code":1000,"msg":None,"data":None}
        try:

            res["data"] = {"数据":123}
        except exceptions as e:
            res["msg"] = "请求出现错误:%s"%e
            res["code"] = 1002
        return JsonResponse(res)
views.py

 

c. 认证类必须继承BaseAuthentication

1. 认证类,必须继承:from rest_framework.authentication import BaseAuthentication
2. 其他认证类:BasicAuthentication

d. 全局使用在配置文件中配置,单个CBV使用或者不用认证在类中重写 authentication_classes=[] 即可

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES':['appo1.utils.auth.Myauthenticat'],
    # 'UNAUTHENTICATED_USER':lambda :'匿名用户',  #定义是匿名用户时request.user的值
    'UNAUTHENTICATED_USER':None,  #定义是匿名用户时request.user的值  request.user = None
    'UNAUTHENTICATED_TOKEN':None  #定义是匿名用户时request.auth的值  request.auth = None
}

e. 三种返回值

- 返回值:
	- None,我不管了,下一认证来执行。
	- raise exceptions.AuthenticationFailed('用户认证失败') # from rest_framework import exceptions
	- (元素1,元素2)  # 元素1赋值给request.user; 元素2赋值给request.auth 

f. 源码流程

- 调用 dispatch
    - 封装request	
    - 调用 initial
	 - 调用 perform_authentication  认证
		- 调用request.user
                     - 获取定义的认证类(全局/局部),通过列表生成式创建对象列表。
                            - 循环调用对象的authenticate方法
-如果通过认证返回一个元组,把第一个元素赋值给request.user,
                   第二个元素赋值给request.auth

  

2. 权限认证

 

a. 权限认证,继承 BasePermission实现 has_permission方法

from rest_framework.permissions import BasePermission
from rest_framework.permissions import BasePermission


class SVIPPermission(BasePermission):
    message = "必须是SVIP才可以访问"  # 定义没权限时返回的信息

    def has_permission(self, request, view):
        # 这里的request是封装过后的request,并且通过认证后request.user和request.auth已经有值
        if request.user.user_type == 3:
            return True
        else:
            return False

    def has_object_permission(self, request, view, obj):
        pass

#在需要的CBV中添加属性 permission_classes = [SVIPPermission,]
views.py

 

has_object_permission(self,request,view,obj):  对单个对象进行验证

访问详细页时走RetrieveModelMixin里的get_obj() 方法 最终调用到has_object_permission方法

    # GenericAPIView中get_object时调用
    def has_object_permission(self, request, view, obj):
        """
        视图继承GenericAPIView,并在其中使用get_object时获取对象时,触发单独对象权限验证
        Return `True` if permission is granted, `False` otherwise.
        :param request: 封装后的requrst 
        :param view: 视图函数对象
        :param obj: model对象
        :return: True有权限;False无权限
        """
        if request.user == "管理员":
            return True

  

 

b. 全局使用配置,不需权限认证的类中重写 permission_classes = []  即可

REST_FRAMEWORK = {
    #认证
    'DEFAULT_AUTHENTICATION_CLASSES':['app01.utils.auth.Myauthenticat'],
    # 'UNAUTHENTICATED_USER':lambda :'匿名用户',  #定义是匿名用户时request.user的值
    'UNAUTHENTICATED_USER':None,  #定义是匿名用户时request.user的值  request.user = None
    'UNAUTHENTICATED_TOKEN':None,  #定义是匿名用户时request.auth的值  request.auth = None

    #权限
    "DEFAULT_PERMISSION_CLASSES":['app01.utils.permission.SVIPPermission'],
}  

c. 返回值

   - 返回True 通过验证

   - 返回 False  不通过验证,message=‘xxx’ 可自定义错误信息

d. 源码流程

-调用dispatch
-对原来对request进行封装,里面包含原来对request
-调用inital进入验证
-调用check_permissions方法
-获取定义对权限类,全局或局部的,用列表生成式创建对象列表 -循环调用所有对定制权限类的has_permission方法 返回True通过,否则抛出异常

  

 

3. 访问 次数/频率 限制

 

a. 自定制的根据ip限制访问频率类

from rest_framework.throttling import BaseThrottle
import time

res = {}

class VisitThrottle(BaseThrottle):
    """
    每分钟小于三次
    """
    def __init__(self):
        self.history = None
    def allow_request(self, request, view):
        current_time = time.time()
        ip = request._request.META.get("REMOTE_ADDR")
        if ip not in res:
            res[ip] = [current_time,]
            return True

        history=res.get(ip)
        self.history = history
        while history and history[-1] < current_time-60:
            history.pop()

        if len(history)<3:
            history.insert(0,current_time)
            return True

    # return True    # 表示可以继续访问
    # return False # 表示访问频率太高,被限制

    def wait(self):
        """
        显示剩余事件
        """
        current_time = time.time()
        return 60 - (current_time - self.history[-1])


class Orderview(APIView):
    
    #频率限制
    throttle_classes = [VisitThrottle,]

    def get(self, request):
        # self.dispatch()
        print(request.user, request.auth)  # UserInfo object ,b5c552a6c744818c8fe10d7f8554a4c6
        res = {"code": 1000, "msg": None, "data": None}
        try:

            res["data"] = {"数据": 123}
        except exceptions as e:
            res["msg"] = "请求出现错误:%s" % e
            res["code"] = 1002
        return JsonResponse(res)

    def throttled(self, request, wait):
        """
        访问次数被限制时,定制错误信息
        """

        class Throttled(exceptions.Throttled):
            default_detail = '请求被限制.'
            extra_detail_singular = '请 {wait} 秒之后再重试.'
            extra_detail_plural = '请 {wait} 秒之后再重试.'

        raise Throttled(wait)
Views.py

 

REST_FRAMEWORK = {
    #全局认证
    'DEFAULT_AUTHENTICATION_CLASSES':['app01.utils.auth.Myauthenticat'],
    # 'UNAUTHENTICATED_USER':lambda :'匿名用户',  #定义是匿名用户时request.user的值
    'UNAUTHENTICATED_USER':None,  #定义是匿名用户时request.user的值  request.user = None
    'UNAUTHENTICATED_TOKEN':None,  #定义是匿名用户时request.auth的值  request.auth = None

    #全局权限
    "DEFAULT_PERMISSION_CLASSES":['app01.utils.permission.SVIPPermission'],

    #全局控制频率
    'DEFAULT_THROTTLE_CLASSES':['app01.utils.throttle.VisitThrottle'],
    #内置类截流配置
    'DEFAULT_THROTTLE_RATES':{
        'zhou':'4/m', #每分钟4次
    }
}
settings.py

 

b. 内置控制频率类(推荐使用)

from rest_framework.throttling import SimpleRateThrottle


class VisitThrottle(SimpleRateThrottle):
    scope = 'zhou'  # 在配置文件里设置的频率

    def get_cache_key(self, request, view):
        """
        :param request:封装后的request
        :param view:
        :return: 字典的键,用这个键作为限制的根据,例如限制一个ip的频率return self.get_ident(request)
                限制一个账号的频率 return request.user.username
        """
        return self.get_ident(request)  # 得到IP


class Orderview(APIView):
    # 频率控制
    throttle_classes = [VisitThrottle, ]

    def get(self, request):
        # self.dispatch()
        print(request.user, request.auth)  # UserInfo object ,b5c552a6c744818c8fe10d7f8554a4c6
        res = {"code": 1000, "msg": None, "data": None}
        try:

            res["data"] = {"数据": 123}
        except exceptions as e:
            res["msg"] = "请求出现错误:%s" % e
            res["code"] = 1002
        return JsonResponse(res)

    def throttled(self, request, wait):
        """
        访问次数被限制时,定制错误信息
        """

        class Throttled(exceptions.Throttled):
            default_detail = '请求被限制.'
            extra_detail_singular = '请 {wait} 秒之后再重试.'
            extra_detail_plural = '请 {wait} 秒之后再重试.'

        raise Throttled(wait)
Views.py

 

REST_FRAMEWORK = {
    #全局认证
    'DEFAULT_AUTHENTICATION_CLASSES':['app01.utils.auth.Myauthenticat'],
    # 'UNAUTHENTICATED_USER':lambda :'匿名用户',  #定义是匿名用户时request.user的值
    'UNAUTHENTICATED_USER':None,  #定义是匿名用户时request.user的值  request.user = None
    'UNAUTHENTICATED_TOKEN':None,  #定义是匿名用户时request.auth的值  request.auth = None

    #全局权限
    "DEFAULT_PERMISSION_CLASSES":['app01.utils.permission.SVIPPermission'],

    #全局控制频率
    'DEFAULT_THROTTLE_CLASSES':['app01.utils.throttle.VisitThrottle'],
    #内置类截流配置
    'DEFAULT_THROTTLE_RATES':{
        'zhou':'4/m', #每分钟4次
    }
}
settings.py

 

c. 源码流程

-调用dispatch
-对原来对request进行封装,里面包含原来对request
-调用inital进入验证
-调用check_throttles方法
-
获取定义的节流类,全局或局部的,用列表生成式创建对象列表 -在这个方法里循环调用所有对定制限制频率类的allow_request方法 返回True通过,否则抛出异常,调用wait方法返回具体的剩余时间

  

 

4. 版本

 

a.  基于url的get传参方式,例如?version=v1

自定义

    url(r'^users/', views.UserView.as_view(),name='users_list'),
urls.py
from django.urls import reverse

from rest_framework.versioning import BaseVersioning


class ParamVersion(BaseVersioning):
    """
    在URL的GET参数中获取版本信息
    """
    def determine_version(self, request, *args, **kwargs):
        #在URL获得版本version的值 query_params相当于_request.GET
        version = request.query_params.get("version")
        print(version,"version")

        return version  #返回版本号,在request.version就可以取到



class UserView(APIView):
    versioning_class = ParamVersion  # 注意不要用列表

    def get(self,request,*args,**kwargs):

        # 获取版本
        print(request.version) #v1
        # 获取版本管理的类
        print(request.versioning_scheme) #<api.views.ParamVersion object at 0x105cb2860>

        # 反向生成URL,url需要参数时记得给参数
        reverse_url1 = request.versioning_scheme.reverse(viewname='users_list',request=request,kwargs={"version":"v2"})
        reverse_url2 =reverse('users_list',kwargs={'version':"v1"})

        print(reverse_url1) #http://127.0.0.1:8000/api/v2/users/
        print(reverse_url2) #/api/v1/users/
        return HttpResponse("用户列表")
Views.py

 

内置的类QueryParameterVersioning

    url(r'^users/', views.UserView.as_view(),name='users_list'),
urls.py
REST_FRAMEWORK = {
    'DEFAULT_VERSION': 'v1',            # 默认版本
    'ALLOWED_VERSIONS': ['v1', 'v2'],   # 允许的版本
    'VERSION_PARAM': 'version'          # URL中获取值的key
}
settings.py
from rest_framework.versioning import QueryParameterVersioning
class UserView(APIView):
    versioning_class = QueryParameterVersioning  # 注意不要用列表

    def get(self,request,*args,**kwargs):

        # 获取版本
        print(request.version) #v1
        # 获取版本管理的类
        print(request.versioning_scheme) #<rest_framework.versioning.QueryParameterVersioning object at 0x105ce92e8>

        # 反向生成URL,url需要参数时记得给参数
        reverse_url1 = request.versioning_scheme.reverse(viewname='users_list',request=request)
        reverse_url2 =reverse('users_list')

        print(reverse_url1) #http://127.0.0.1:8000/api/users/?version=v1
        print(reverse_url2) #/api/users/
        return HttpResponse("用户列表")
Views.py

b. 基于URL的正则方式  例如:api/v1/users/ (推荐使用)

使用内置类URLPathVersioning

    url(r'^(?P<version>[v1|v2]+)/users/$', views.UserView.as_view(), name='users_list'),

#允许的版本有v1和v2
urls.py
from rest_framework.versioning import URLPathVersioning
class UserView(APIView):
    versioning_class = URLPathVersioning  # 注意不要用列表

    def get(self,request,*args,**kwargs):

        # 获取版本
        print(request.version) #v2
        # 获取版本管理的类
        print(request.versioning_scheme) # <rest_framework.versioning.URLPathVersioning object at 0x105cf07b8>>

        # 自动反向生成URL
        reverse_url1 = request.versioning_scheme.reverse(viewname='users_list',request=request)
        #手动生成url,需要传参数
        reverse_url2 =reverse('users_list',kwargs={'version':'v1'})

        print(reverse_url1) #http://127.0.0.1:8000/api/v2/users/
        print(reverse_url2) #/api/v1/users/
        return HttpResponse("用户列表")
Views.py

 

如果想全局使用直接在配置文件内配置(推荐使用)

    #版本,基于URL的正则方式(全局)
    "DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.URLPathVersioning",

c. 其他方式

AcceptHeaderVersioning  #基于 accept 请求头方式
NamespaceVersioning  #基于django路由系统的namespace
HostNameVersioning  #基于主机名方法

 

 

 

5. 解析器

 

根据请求头 content-type 选择对应的解析器就请求体内容进行处理。

request.POST中有值的条件

1. 请求头要求:
	Content-Type: application/x-www-form-urlencoded
	PS: 如果请求头中的 Content-Type: application/x-www-form-urlencoded,request.POST中才有值(去request.body中解析数据)。
2. 数据格式要求:
	name=alex&age=18&gender=男
		

 

各种解析器:  

仅处理请求头content-type为application/json的请求体  <JSONParser>  #发送json数据时仅request.data有值
仅处理请求头content-type为application/x-www-form-urlencoded 的请求体<FormParser>  #发送表单数据时仅request.POST 和request.data 都有值
仅处理请求头content-type为multipart/form-data(二进制数据)的请求体<MultiPartParser>  #request.data中有数据,如果是文件request.FILES中有
仅上传文件<FileUploadParser>  #上传文件时request.FILES取值

# application/x-www-form-urlencoded 或 multipart/form-data时,request.POST中才有值
#导入 from rest_framework.parsers import JSONParser,FormParser,MultiPartParser,FileUploadParser 

#配置 parser_classes = [JSONParser,FormParser,MultiPartParser,FileUploadParser]

取值:

request.data
request.POST
request.FILES 

全局使用配置

REST_FRAMEWORK = {
    'DEFAULT_PARSER_CLASSES':[
        'rest_framework.parsers.JSONParser',
        'rest_framework.parsers.FormParser',
        'rest_framework.parsers.MultiPartParser',
        'rest_framework.parsers.FileUploadParser'
    ]

}

注意:个别特殊的值可以通过Django的request对象 request._request 来进行获取

 

 

6. 序列化

 

序列化用于对用户请求数据进行验证和数据进行序列化。

a. 基于serializers.Serializer自定义  

from rest_framework import serializers

class UserSerializer(serializers.Serializer):
    id = serializers.IntegerField()
    username=serializers.CharField()
    password = serializers.CharField()
    user_type = serializers.CharField(source='get_user_type_display') #显示choices的中文
    group = serializers.CharField(source='group.title') #外健字段
    # roles = serializers.CharField(source='roles.all') #结果是一个queryset
    roles = serializers.SerializerMethodField()  #要先显示详细自定义,写一个 get_ 拼接的函数

    def get_roles(self,row):
        """
        :param row: 就是一个model类对象
        :return: 返回什么显示什么
        """
        queryset = row.roles.all()
        ret = []
        for obj in queryset:
            ret.append({"id":obj.pk,"title":obj.title})
        return ret




class UserView(APIView):

    def get(self, request, *args, **kwargs):

        obj = UserInfo.objects.all()
        ser = UserSerializer(instance=obj,many=True)

        # ser.data 就是已经处理完的可序列化的数据
        return HttpResponse(json.dumps(ser.data))

    def post(self,request, *args, **kwargs):


        return HttpResponse("用户列表post")

b. 基于serializers.SerializerMethodField 自动生成字段

自己写类:

class MyField(serializers.CharField):

    def to_representation(self, value):
        print(value,"value")
        # zhou value
        # yu value
        return value

from rest_framework import serializers

class UserSerializer(serializers.ModelSerializer):
    user_type = serializers.CharField(source='get_user_type_display')
    group = serializers.CharField(source='group.title')
    group_id = serializers.IntegerField(source='group.pk')
    roles = serializers.SerializerMethodField()
    name = MyField(source='username')
    class Meta:
        model=models.UserInfo
        # fields='__all__'  #处理所有的字段,但一些关联字段只显示id,要想详细的数据自己写
        fields=["id","name","user_type",'group',"group_id","roles"]

    def get_roles(self,row):
        """
        :param row: 就是一个model类对象
        :return: 返回什么显示什么
        """
        queryset = row.roles.all()
        ret = []
        for obj in queryset:
            ret.append({"id":obj.pk,"title":obj.title})
        return ret

class UserView(APIView):

    def get(self, request, *args, **kwargs):

        obj = models.UserInfo.objects.all()
        ser = UserSerializer(many=True,instance=obj)
        # ser.data 就是已经处理完的可序列化的数据

        return HttpResponse(json.dumps(ser.data,ensure_ascii=False))

    def post(self,request, *args, **kwargs):


        return HttpResponse("用户列表post")
Views.py

 

[
    {
        "id": 1,
        "name": "zhou",
        "user_type": "SVIP",
        "group": "A组",
        "group_id": 1,
        "roles": [
            {
                "title": "兔子",
                "id": 1
            },
            {
                "title": "萝莉",
                "id": 2
            }
        ]
    },
    {
        "id": 2,
        "name": "yu",
        "user_type": "普通用户",
        "group": "B组",
        "group_id": 2,
        "roles": [
            {
                "title": "兔子",
                "id": 1
            }
        ]
    }
]
返回的数据

 

使用depth 自动序列化链表:

from rest_framework import serializers

class UserSerializer(serializers.ModelSerializer):
    user_type = serializers.CharField(source='get_user_type_display')
    class Meta:
        model=models.UserInfo
        # fields='__all__'  #处理所有的字段
        fields=["id","username","user_type",'group',"roles"]
        depth = 2  # 深度 官方建议0~10


class UserView(APIView):

    def get(self, request, *args, **kwargs):

        obj = models.UserInfo.objects.all()
        ser = UserSerializer(many=True,instance=obj)
        # ser.data 就是已经处理完的可序列化的数据

        return HttpResponse(json.dumps(ser.data,ensure_ascii=False))

    def post(self,request, *args, **kwargs):


        return HttpResponse("用户列表post")
Views.py
[
    {
        "id": 1,
        "username": "zhou",
        "user_type": "SVIP",
        "group": {
            "id": 1,
            "title": "A组"
        },
        "roles": [
            {
                "id": 1,
                "title": "兔子"
            },
            {
                "id": 2,
                "title": "萝莉"
            }
        ]
    },
    {
        "id": 2,
        "username": "yu",
        "user_type": "普通用户",
        "group": {
            "id": 2,
            "title": "B组"
        },
        "roles": [
            {
                "id": 1,
                "title": "兔子"
            }
        ]
    }
]
返回的数据

 

生成URl:

from django.conf.urls import url
from api import views
urlpatterns = [
    # url(r'^admin/', admin.site.urls),

    url(r'^(?P<version>[v1|v2]+)/users/$', views.UserView.as_view(), name='users_list'),
    url(r'^(?P<version>[v1|v2]+)/group/(?P<group_id>\d+)$', views.UserView.as_view(), name='group_detail'),

]
urls.py

 

from rest_framework import serializers

class UserSerializer(serializers.ModelSerializer):
    user_type = serializers.CharField(source='get_user_type_display')
    # 生成URl,view_name是别名,lookup_field是查询依据的字段,lookup_url_kwarg是url里参数的名字
    group = serializers.HyperlinkedIdentityField(view_name='group_detail',lookup_field='group_id',lookup_url_kwarg='group_id')
    class Meta:
        model=models.UserInfo
        # fields='__all__'  #处理所有的字段
        fields=["id","username","user_type",'group',"roles"]
        depth = 2  # 深度 官方建议0~10



class UserView(APIView):

    def get(self, request, *args, **kwargs):

        obj = models.UserInfo.objects.all()
        ser = UserSerializer(many=True,instance=obj,context={"request":request})
        #生成url时需要加上context={"request":request}
        # ser.data 就是已经处理完的可序列化的数据

        return HttpResponse(json.dumps(ser.data,ensure_ascii=False))

    def post(self,request, *args, **kwargs):


        return HttpResponse("用户列表post")
views.py
[
    {
        "id": 1,
        "username": "zhou",
        "user_type": "SVIP",
        "group": "http://127.0.0.1:8000/api/v2/group/1",
        "roles": [
            {
                "id": 1,
                "title": "兔子"
            },
            {
                "id": 2,
                "title": "萝莉"
            }
        ]
    },
    {
        "id": 2,
        "username": "yu",
        "user_type": "普通用户",
        "group": "http://127.0.0.1:8000/api/v2/group/2",
        "roles": [
            {
                "id": 1,
                "title": "兔子"
            }
        ]
    }
]
返回的数据

 

请求数据校验:

class PasswordValidator(object):
    def __init__(self, base):
        self.base = str(base)

    def __call__(self, value):
        if not value.startswith(self.base):
            message = '标题必须以 %s 为开头。' % self.base
            raise serializers.ValidationError(message)

    def set_context(self, serializer_field):
        """
        This hook is called by the serializer instance,
        prior to the validation call being made.
        """
        # 执行验证之前调用,serializer_fields是当前字段对象
        pass

class UserSerializerPOST(serializers.ModelSerializer):
    username=serializers.CharField(max_length=6,error_messages={"max_length":"最大长度为2"})
    password = serializers.CharField(error_messages={'required': '密码不能为空'}, validators=[PasswordValidator('666')])
    class Meta:
        model = models.UserInfo
        fields='__all__'
        # fields=["username","password","roles"]


    def validate_username(self,value):
        print(value,"value") #value就是username的值
        from rest_framework import exceptions
        if not value.startswith("zhou"):
            raise exceptions.ValidationError("必须以zhou开头")
        else:
            return value


class UserView(APIView):


    def post(self,request, *args, **kwargs):

        ser = UserSerializerPOST(data=request.data)
        if ser.is_valid():
            # 通过校验
            print(ser.validated_data,"validated_data")
            return HttpResponse("ok")

        else:
            print(ser.errors,"errors")

            return HttpResponse(json.dumps(ser.errors))

  

 

7. 分页

 

a. 根据页码进行分页,看第n页,每页显示n条数据

from rest_framework.pagination import PageNumberPagination,LimitOffsetPagination,CursorPagination


class MyPageNumberPagination(PageNumberPagination):
    page_size = 2  # 每页显示的条数
    page_query_param = 'page'  # url里的参数key
    page_size_query_param = "size"  #可以指定每页显示多少条数据的参数 默认None
    max_page_size = 5  #每页显示数据的最大限制


class UserSerializer(ModelSerializer):
    class Meta:
        model=models.Role
        fields="__all__"


class UserView(APIView):


    def get(self,request, *args, **kwargs):
        # 获取所有数据
        obj = models.Role.objects.all()
        # 创建分页器对象
        pg = MyPageNumberPagination()
        # 在数据库中获取分页的数据
        pager_roles = pg.paginate_queryset(queryset=obj,request=request,view=self)
        # 对数据序列化
        ser = UserSerializer(instance=pager_roles,many=True)

        return Response(ser.data)

  

b. 位置和个数分页,在n个位置,往后查看n条

from rest_framework.pagination import LimitOffsetPagination,CursorPagination


class MyPageNumberPagination(LimitOffsetPagination):

    default_limit = 2 # 每页显示的条数
    limit_query_param = 'limit' # 每页查看多少条数据
    offset_query_param = 'offset'  #从第多少个开始
    max_limit = 5 # 每页显示数据的最大限制


class UserSerializer(ModelSerializer):
    class Meta:
        model=models.Role
        fields="__all__"


class UserView(APIView):


    def get(self,request, *args, **kwargs):
        # 获取所有数据
        obj = models.Role.objects.all()
        # 创建分页器对象
        pg = MyPageNumberPagination()
        # 在数据库中获取分页的数据
        pager_roles = pg.paginate_queryset(queryset=obj,request=request,view=self)
        # 对数据序列化
        ser = UserSerializer(instance=pager_roles,many=True)

        return Response(ser.data)

  

c. 游标分页,加密分页,显示上一页和下一页的URL

from rest_framework.pagination import CursorPagination


class MyPageNumberPagination(CursorPagination):
    cursor_query_param = 'cursor' # url里GET参数
    page_size = 5 # 每页显示几个
    ordering = 'id' # 按什么字段排序



class UserSerializer(ModelSerializer):
    class Meta:
        model=models.Role
        fields="__all__"


class UserView(APIView):


    def get(self,request, *args, **kwargs):
        # 获取所有数据
        obj = models.Role.objects.all()
        # 创建分页器对象
        pg = MyPageNumberPagination()
        # 在数据库中获取分页的数据
        pager_roles = pg.paginate_queryset(queryset=obj,request=request,view=self)
        # 对数据序列化
        ser = UserSerializer(instance=pager_roles,many=True)

        # return Response(ser.data)
        return pg.get_paginated_response(ser.data) #使用这个方法显示上一页和下一页的url

d. 可以在配置文件设置全局配置

"PAGE_SIZE":2, #每页数据条数

  

8. 视图

 

a. GenericAPIView

from rest_framework.response import Response
from rest_framework.serializers import ModelSerializer
from rest_framework.pagination import CursorPagination


class MyPageNumberPagination(CursorPagination):
    """
    分页
    """
    cursor_query_param = 'cursor' # url里GET参数
    page_size = 5 # 每页显示几个
    ordering = 'id' # 按什么字段排序



class UserSerializer(ModelSerializer):
    """
    序列化
    """
    class Meta:
        model=models.Role
        fields="__all__"


from rest_framework.generics import GenericAPIView

class UserView(GenericAPIView):
    queryset = models.Role.objects.all()
    serializer_class = UserSerializer
    pagination_class = MyPageNumberPagination


    def get(self,request, *args, **kwargs):
        # 获取数据
        obj = self.get_queryset() # 相当于models.Role.objects.all()

        # 获取分页的数据
        pager_roles = self.paginate_queryset(obj)
        # 对数据序列化
        ser = self.get_serializer(instance=pager_roles,many=True)

        # return Response(ser.data)
        return self.get_paginated_response(ser.data) #使用这个方法显示上一页和下一页的url

  

b. GenericViewSet

from django.conf.urls import url
from api import views
urlpatterns = [
    # url(r'^admin/', admin.site.urls),

    url(r'^(?P<version>[v1|v2]+)/users/$', views.UserView.as_view({"get":"list","post":"add"}), name='users_list'),
    url(r'^(?P<version>[v1|v2]+)/detail/(?P<pk>\d+)/$', views.UserView.as_view({"get":"list","delete":"delete","put":"edit"}), name='xiangxi'),

]
urls.py
from rest_framework.response import Response
from rest_framework.serializers import ModelSerializer
from rest_framework.pagination import CursorPagination,PageNumberPagination


class MyPageNumberPagination(PageNumberPagination):
    """
    分页
    """
    page_size = 2  # 每页显示的条数
    page_query_param = 'page'  # url里的参数key
    page_size_query_param = "size"  #可以指定每页显示多少条数据的参数 默认None
    max_page_size = 5  #每页显示数据的最大限制



class UserSerializer(ModelSerializer):
    """
    序列化
    """
    class Meta:
        model=models.Role
        fields="__all__"


from rest_framework.generics import GenericAPIView
from rest_framework.viewsets import GenericViewSet,ModelViewSet,ViewSetMixin
from rest_framework.mixins import ListModelMixin,CreateModelMixin

class UserView(GenericViewSet):
    queryset = models.Role.objects.all()
    serializer_class = UserSerializer
    pagination_class = MyPageNumberPagination


    def list(self,request, *args, **kwargs):
        if kwargs.get("pk"):
            return HttpResponse("详细")
        # 获取数据
        obj = self.get_queryset() # 相当于models.Role.objects.all()

        # 获取分页的数据
        pager_roles = self.paginate_queryset(obj)
        # 对数据序列化
        ser = self.get_serializer(instance=pager_roles,many=True)

        return Response(ser.data)
        # return self.get_paginated_response(ser.data) #使用这个方法显示上一页和下一页的url


    def add(self,request, *args, **kwargs):

        ser = UserSerializer(data=request.data)
        if ser.is_valid():
            # 通过校验
            print(ser.validated_data,"validated_data")
            return HttpResponse("ok")

        else:
            print(ser.errors,"errors")

            return HttpResponse(json.dumps(ser.errors))

    def delete(self, request, *args, **kwargs):
        print(kwargs["pk"],"pk")
        return HttpResponse("删除")

    def edit(self, request, *args, **kwargs):

        return HttpResponse("编辑")
Views.py

c. ModelViewSet(自定义URL)

from django.conf.urls import url
from api import views
urlpatterns = [
    # url(r'^admin/', admin.site.urls),

    url(r'^(?P<version>[v1|v2]+)/users/$', views.UserView.as_view({"get":"list","post":"create"}), name='users_list'),
    url(r'^(?P<version>[v1|v2]+)/users/(?P<pk>\d+)/$', views.UserView.as_view({'get': 'retrieve', 'put': 'update', 'patch': 'partial_update', 'delete': 'destroy'}), name='xiangxi'),

]
urls.py
from rest_framework.response import Response
from rest_framework.serializers import ModelSerializer
from rest_framework.pagination import PageNumberPagination


class MyPageNumberPagination(PageNumberPagination):
    """
    分页
    """
    page_size = 2  # 每页显示的条数
    page_query_param = 'page'  # url里的参数key
    page_size_query_param = "size"  #可以指定每页显示多少条数据的参数 默认None
    max_page_size = 5  #每页显示数据的最大限制



class UserSerializer(ModelSerializer):
    """
    序列化
    """
    class Meta:
        model=models.Role
        fields="__all__"



from rest_framework.viewsets import ModelViewSet

class UserView(ModelViewSet):
    queryset = models.Role.objects.all()
    serializer_class = UserSerializer
Views.py 

d. ModelViewSet(rest framework路由)

from django.conf.urls import url,include
from rest_framework import routers
from . import views


router = routers.DefaultRouter()
router.register(r'users', views.UserView)


# Wire up our API using automatic URL routing.
# Additionally, we include login URLs for the browsable API.
urlpatterns = [
    url(r'^(?P<version>[v1|v2]+)/', include(router.urls)),
]
urls.py
from rest_framework.response import Response
from rest_framework.serializers import ModelSerializer
from rest_framework.pagination import PageNumberPagination


class MyPageNumberPagination(PageNumberPagination):
    """
    分页
    """
    page_size = 2  # 每页显示的条数
    page_query_param = 'page'  # url里的参数key
    page_size_query_param = "size"  #可以指定每页显示多少条数据的参数 默认None
    max_page_size = 5  #每页显示数据的最大限制



class UserSerializer(ModelSerializer):
    """
    序列化
    """
    class Meta:
        model=models.Role
        fields="__all__"



from rest_framework.viewsets import ModelViewSet

class UserView(ModelViewSet):
    queryset = models.Role.objects.all()
    serializer_class = UserSerializer
Views.py

视图继承关系

 

 

9. 路由

 

a. 自定义路由

from django.conf.urls import url, include
from web.views import s11_render

urlpatterns = [
    url(r'^test/$', s11_render.TestView.as_view()),
    url(r'^test\.(?P<format>[a-z0-9]+)$', s11_render.TestView.as_view()),
    url(r'^test/(?P<pk>[^/.]+)/$', s11_render.TestView.as_view()),
    url(r'^test/(?P<pk>[^/.]+)\.(?P<format>[a-z0-9]+)$', s11_render.TestView.as_view())
]

urls.py
urls.py
from rest_framework.views import APIView
from rest_framework.response import Response
from .. import models


class TestView(APIView):
    def get(self, request, *args, **kwargs):
        print(kwargs)
        print(self.renderer_classes)
        return Response('...')
View.py

b.半自动路由

from django.conf.urls import url, include
from web.views import s10_generic

urlpatterns = [
    url(r'^test/$', s10_generic.UserViewSet.as_view({'get': 'list', 'post': 'create'})),
    url(r'^test/(?P<pk>\d+)/$', s10_generic.UserViewSet.as_view(
        {'get': 'retrieve', 'put': 'update', 'patch': 'partial_update', 'delete': 'destroy'})),
]
urls.py

 

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.viewsets import ModelViewSet
from rest_framework import serializers
from .. import models


class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.UserInfo
        fields = "__all__"


class UserViewSet(ModelViewSet):
    queryset = models.UserInfo.objects.all()
    serializer_class = UserSerializer
views.py

 

c. 全自动路由

from django.conf.urls import url,include
from rest_framework import routers
from . import views


router = routers.DefaultRouter()
router.register(r'users', views.UserView)


# Wire up our API using automatic URL routing.
# Additionally, we include login URLs for the browsable API.
urlpatterns = [
    url(r'^(?P<version>[v1|v2]+)/', include(router.urls)),
]
from rest_framework.serializers import ModelSerializer

class UserSerializer(ModelSerializer):
    """
    序列化
    """
    class Meta:
        model=models.Role
        fields="__all__"



from rest_framework.viewsets import ModelViewSet

class UserView(ModelViewSet):
    queryset = models.Role.objects.all()
    serializer_class = UserSerializer
View.py

 

 

10. 渲染器

 

from rest_framework.serializers import ModelSerializer

class UserSerializer(ModelSerializer):
    """
    序列化
    """
    class Meta:
        model=models.Role
        fields="__all__"


from rest_framework.renderers import JSONRenderer, BrowsableAPIRenderer  # JSON和浏览器的渲染器
from rest_framework.viewsets import ModelViewSet

class UserView(ModelViewSet):
    renderer_classes = [JSONRenderer,BrowsableAPIRenderer]
    queryset = models.Role.objects.all()
    serializer_class = UserSerializer

全局配置

REST_FRAMEWORK = {
    #全局认证
    # 'DEFAULT_AUTHENTICATION_CLASSES':['app01.utils.auth.Myauthenticat'],
    # 'UNAUTHENTICATED_USER':lambda :'匿名用户',  #定义是匿名用户时request.user的值
    'UNAUTHENTICATED_USER':None,  #定义是匿名用户时request.user的值  request.user = None
    'UNAUTHENTICATED_TOKEN':None,  #定义是匿名用户时request.auth的值  request.auth = None

    #全局权限
    # "DEFAULT_PERMISSION_CLASSES":['app01.utils.permission.SVIPPermission'],

    #全局控制频率
    # 'DEFAULT_THROTTLE_CLASSES':['app01.utils.throttle.VisitThrottle'],
    #内置类截流配置
    'DEFAULT_THROTTLE_RATES':{
        'zhou':'4/m', #每分钟4次
    },

    #版本,基于get传参
    'DEFAULT_VERSION': 'v1',  # 默认版本
    'ALLOWED_VERSIONS': ['v1', 'v2'],  # 允许的版本
    'VERSION_PARAM': 'version', # URL中获取值的key
    #版本,基于URL的正则方式(全局)
    "DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.URLPathVersioning",

    #全局设置的解析器
    'DEFAULT_PARSER_CLASSES':[
        'rest_framework.parsers.JSONParser',
        'rest_framework.parsers.FormParser',
        'rest_framework.parsers.MultiPartParser',
        'rest_framework.parsers.FileUploadParser'
    ],
    # 分页
    "PAGE_SIZE":2, #每页数据条数
    # 渲染器
    "DEFAULT_RENDERER_CLASSES": [
        'rest_framework.renderers.JSONRenderer',
        'rest_framework.renderers.BrowsableAPIRenderer',
    ]

}

 

 

错误处理

 

'CourseView' should either include a `queryset` attribute, or override the `get_queryset()` method.

'CourseView' should either include a `serializer_class` attribute, or override the `get_serializer_class()` method.

 

原因:

如果我们试图类,继承了:GenericAPIView 或 GenericAPIView的派生类,只要执行 get_queryset 时,可能会报错(queryset = None)

如果我们试图类,继承了:GenericAPIView 或 GenericAPIView的派生类,只要执行 get_serializer 时,可能会报错(serializer_class = None)

 

有时加上渲染器 BrowsableAPIRenderer浏览器访问时会报错,postman和api则不会

分析原因:

  BrowsableAPIRenderer内部调用了get_queryset

 

解决方法一: 

加上属性queryset = ...  serializer_class = ...

 

解决方法二:

#继承APIView自己写,如果需要as_view里的对应关系可继承ViewSetMixin
#from rest_framework.viewsets import GenericViewSet,ViewSetMixin
#from rest_framework.views import APIView

	url(r'^(?P<version>[v1|v2]+)/auth/$', views.AuthView.as_view()),
	class MyView(APIView):
		...
		
		
	url(r'^(P<version[v1|v2]+)/course/$',views.CourseView.as_view({'get':'fun1'})),
	class CourseView(ViewSetMixin,APIView):

  

 

posted @ 2018-03-05 17:13  选择远方,风雨兼程。  阅读(687)  评论(0编辑  收藏  举报