Django Rest-Framework

零,DRF框架预备知识

  APIView与View的区别

    APIView继承了View

    csrf的豁免

    重现封装request对象

      原生的request赋值给了request._request

      request.query_params封装了原生的request.GET

      request.data封装了除GET外的所有信息(request.POST,request.files)

    原生的request为WSGIRequest的实例化对象

  响应对象Response

    携带HTTP表中状态码

    做模板的渲染

一,restful规范

  REST风格

    表述状态转移:web交换方案,前后端的数据传输的设计思想

    资源的概念:在web中只要又被引用的必要都是资源

    URI

      URI:同一资源标识符

      URL:统一资源定位符,URI的子集

    统一资源接口

      根据HTTP请求方式的不同对资源进行不同的操作

      遵循HTTP请求的语义

    资源的表述

      前后端的传输叫资源的表述

      传输的不是资源的本身,而是资源的某一种表述形式

    资源的状态

      前端展示的叫资源的状态

    通过超链接的指引告诉用户接下来有哪些资源状态可以进入

    

    核心思想

      面向资源编程

         每个url就是资源的体现,不体现操作,url命名尽量用名词不要用动词

      根据HTTP请求方式的不同对资源进行不同的操作

    URL体现:

      版本

        htttps://v1.xxx.com

        https://xxx.com/v3

        体现是否为API接口

        https://v1.xxx.com/api

      过滤信息

        https://v1.xxx.com?page=1

      尽量使用HTTPS

    返回值的体现(响应请求)

      携带状态码

        1xx:信息,服务器收到请求,需要请求者继续执行操作

        2xx:成功,操作被成功接收并处理

        3xx:重定向,需要进一步的操作以完成请求

        4xx:客户端错误,请求包含语法错误或无法完成请求

        5xx:服务器错误,服务器在处理请求的过程中发生了错误

      携带错误信息

      返回值

        get 返回查看的所有或单条信息

        post 返回新增的那条数据

        put 返回更新数据

        patch 局部更新,返回更新的那条数据

        delete 返回值为空

      Hypermedia API

           如果遇到需要跳转的情况 携带跳转接口的URL

ret = {
          code: 1000,
          data:{
          id:1,
          name:'小强',
          depart_id:http://www.baidu.com/api/v1/depart/8/
          }
}

二,序列化组件

  1、序列化

    实现流程

      1.如果设置了many=True

      2.把queryset当成可迭代对象去循环,得到每个模型对象

      3.把每个模型对象放入序列化器进行序列化

      4.进行字段匹配,匹配上的字段进行序列化,匹配不上的丢弃

      5.序列化的时候必须满足序列化的所有字段要求

    声明一个序列化器

class BookSerializer(serializers.Serializer):
    id = serializers.IntegerField(required=False)  # required=False 忽略校验
    title = serializers.CharField(max_length=32)
    pub_time = serializers.DateField()

    在视图中引用我们定义的序列化器,序列化我们的queryset数据  

ser_obj = BookSerializer(queryset, many=True) # many=True 支持(当成)可迭代对象,循环遍历并序列化
ser_obj = BookSerializer(models_obj) # 同样支持单个模型对象的序列化
ser_obj.validated_data # 校验通过的数据
return Response(ser_obj.data)

  2、反序列化

    获取前端传过来的数据

    用序列化器进行校验      

 BookSerializer(data=request.data)
if ser_obj.is_valid():
    ser_obj.save() # 调用序列化器中的create方法操作orm创建新对象
    return Response(ser_obj.data)
else:
    return Response

   

  3、序列化以及反序列化的时候字段类型不统一的情况

-- required=False  # 对字段取消校验
-- read_only=True  #  仅序列化是进行校验
-- write_only=True  # 仅反序列化是校验

  4、ModeSerializer 帮我们实现create以及update方法

class BookSerializer(serilalizers.ModelSerializer)
       # SerializerMethodField方法字段,会将钩子方法的返回值给字段
       text = serializers.SerializerMethodField(read_only=True)

        class Meta:
            model = Book # 模型类
            fields = "__all__" / ["",""]
            exclude = [""]  # 排除某些字段
            depth = 1  # 根据你的外键关系找几层,会让你所有的外键变成read_only = True
            extra_kwargs = {
                "字段名称":{参数:值}  # 为自动生成的字段添加参数
            }

# SerializerMethodField的钩子方法定义
def get_字段名称(self,obj):
    obj 是我们循环序列化的每个模型对象
    return 自己想要的数据    

  

  4.字段的校验方法

    多个字段的校验方法,优先级为 低     

def validate(self,attrs):
    # attrs 前端传过来的所有的数据组成的字典
    raise serializers.ValidationError("xxxx")
    return value

    单个字段的校验方法,优先级为 中

def validate_字段名(self,value):
    # value字段的值
    raise serializers.ValidationError("xxxx")
    return value

    自定义校验方法,优先级为 高

# 字段中添加validators指定校验方法
title = serializers.CharField(max_length=32,validators=[my_validate])

def my_validate(value):
    raise serializers.ValidationError("xxxx")
    return value

三,视图组件

  视图的封装

class GenericAPIView(APIView):
    query_set = None
    serializer_class = None

    def get_query_set(self):
        return self.query_set  # 从对象属性开始找

    def get_serializer(self, *args, **kwargs):  # 序列化器实例化时,以传参的方式执行
        return self.serializer_class(*args, **kwargs)


class RetrieveModelMixin(object):
    def retrieve(self, request, pk):
        book_obj = self.get_query_set().filter(pk=pk).first()
        ser_obj = self.get_serializer(book_obj)
        return Response(ser_obj.data)


class ListModelMixin(object):
    def list(self, request):
        # print(self.action_map) # actions:{'get': 'list', 'post': 'create'}
        ser_obj = self.get_serializer(self.get_query_set(), many=True)
        return Response(ser_obj.data)


class CreateModelMixin(object):
    def create(self, request):
        ser_obj = self.get_serializer(data=request.data)
        if ser_obj.is_valid():
            ser_obj.save()
            return Response(ser_obj.data)
        # print(ser_obj.errors)
        return Response(ser_obj.errors)


class UpdateModelMixin(object):
    def update(self, request, pk):
        book_obj = self.get_query_set().filter(pk=pk).first()
        ser_obj = self.get_serializer(instance=book_obj, data=request.data,partial=True) # 不用每个字段强制都要传值
        if ser_obj.is_valid():
            ser_obj.save()
            return Response(ser_obj.data)
        return Response(ser_obj.errors)


class DestoryModelMixin(object):
    def destory(self, request, pk):
        book_obj = self.get_query_set().filter(pk=pk).first()
        if book_obj:
            book_obj.delete()
            return Response("")
        return Response("删除的对象不存在")


class ListCreateModeMixin(GenericAPIView, ListModelMixin, CreateModelMixin): pass


class RetrieveUpdateDestroyModelMixn(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestoryModelMixin): pass

# 继承了ViewSetMixin并使用了它的as_view方法
class ModelViewSet(ViewSetMixin, ListCreateModeMixin, RetrieveUpdateDestroyModelMixn): pass

# 第一种CBV
class BookListAPIView(ListCreateModeMixin):
    query_set = Book.objects.all()
    serializer_class = BookSerializer

    def get(self, request):
        return self.list(request)

    def post(self, request):
        return self.create(request)


class BookEditAPIView(RetrieveUpdateDestroyModelMixn):
    query_set = Book.objects.all()
    serializer_class = BookSerializer

    def get(self, request, id):
        return self.retrieve(request, id)

    def put(self, request, id):
        return self.update(request, id)

    def delete(self, request, id):
        return self.destory(request, id)


# 第二种,解耦后的CBV
class BookModelView(ModelViewSet):
    query_set = Book.objects.all()  # 使用路由系统后,需要queryset
    serializer_class = BookSerializer
# queryset 如果重写视图的话,使用queryset作为参数会被框架放入缓存
# 防止数据不更新,应用调用all()方法重新获取数据
# self.get_queryset()
#     return self.queryset.all()




# 第三种,直接继承框架已定义的ModelViewSet类,
from rest_framework.viewsets import ModelViewSet

class BookModelView(ModelViewSet):
    queryset = Book.objects.all()
    serializer_class = BookSerializer


class PublisherModelView(ModelViewSet):
    queryset = Publisher.objects.all()
    serializer_class = PublisherSerializer


class AuthorModelView(ModelViewSet):
    queryset = Author.objects.all()
    serializer_class = AuthorSerializer

  注意

# 如果不利用ViewSetMixin的as_view方法,对self.get=self.list,如此类推赋值,而是重新的ModelViewSet的话,路由的as_view()方法需要手动穿参,重新指定请求的执行方法
urlpatterns = [
    url(r'^list/$', views.BookModelView.as_view({'get':'list','post':'create'})), # 用新的类来处理,由于没有get,post方法,
    url(r'^list/(?P<id>d+)/$', views.BookModelView.as_view({'get':'retrieve','put':'update','delete':'destroy'})),
]

四,路由组件

# 导入
    from rest_framwork.routers import DefaultRouter
# 实例化
    router = DefaultRouter()
# 注册
    router.register("list",views.BookModelView)  
    # 默认生成的路由都自带参数(pk),所以视图中的方法也要带参数pk
    # actions={'get': 'list', 'post': 'create'} 路由系统自动调用as_view({'get': 'list', 'post': 'create'})
# 把默认生成的路由注册
    urlpattrens += router.urls
# 默认生成的路由都是带参数的!!
    # ^book/ ^list/$ [name='book-list']
    # ^book/ ^list.(?P<format>[a-z0-9]+)/?$ [name='book-list']
    # ^book/ ^list/(?P<pk>[^/.]+)/$ [name='book-detail']
    # ^book/ ^list/(?P<pk>[^/.]+).(?P<format>[a-z0-9]+)/?$ [name='book-detail']
    # ^book/ ^$ [name='api-root']
    # ^book/ ^.(?P<format>[a-z0-9]+)/?$ [name='api-root']

五,版本控制组件

# 实现一个版本控制类
from rest_framework.versioning import BaseVersioning  # 框架提供了版本控制组件

class MyVersion(BaseVersioning):
    def determine_version(self,request,*args,**kwargs):
        # 拿版本号
        version = request.query_params.get("version","v1")
        return version


# 源码:
version,scheme = self.determine_version(request,*args,**kwargs)  # 我们配置的版本控制类一定要有determine_version方法 
    request.version,request.versioning_scheme = version,scheme  # 这个方法的返回值应该是版本号 赋值给request.version
def determine_version(self,request,*args,**kwargs):
    if self.versioning_class is None:
        return (None,None)
        # scheme 为我们自己配置的版本控制类的实例化对象
        scheme = self.versioning_class() # scheme是我们配置的版本控制类的实例化对象 赋值给了request.versioning_scheme
        return (scheme.determine_version(request,*args,**kwargs),scheme)

# 在视图
class VersionView(APIView):
  def get(self,request,version):
    print(request._reuqest.body)
    print(request.version)
    print(request.versioning_scheme)
    if request.verison == 'v1':
      return Response('当前版本号为v1')
    elif request.version == 'v2':
      return Response('当前版本号为v2')
    return Response('当前版本不合法')

# 需要在setting中注册
REST_FRAMEWORK = {
  'DEFAULT_VERSIONING_CLASS':'rest_framework.versioning.URLPathVersioning' # 对应的版本控制类
}

# 其他几种DRF自带的版本控制类 AcceptHeaderVersioning # Accept: application/json; version=1.0 在Accept头附带版本信息 URLPathVersioning # 在URL中附带版本信息,url(r'^(?P<version>[v1|v2]+)/users/$', users_list, name='users-list'), NamespaceVersioning # url(r'^v1/', include('users.urls', namespace='v1')), 在名称空间附带版本信息 HostNameVersioning # Host: v1.example.com 在域名附带版本信息 QueryParameterVersioning # 在url参数GET /something/?version=0.1 HTTP/1.1附带版本信息

六,认证控制组件

from rest_framework.authentication import BaseAuthentication
from AuthDemo.models import UserInfo
from rest_framework.exceptions import AuthenticationFailed

# 认证控制类
class MyAuth(object):
    def authenticate(self,request):
        # 获取当前前端携带的token
        # 对对比这个token师傅合法
        token = request.query_params.get("token","")
        if not token:
            raise AuthenticationFailed("没有携带token")
        user_obj = UserInfo.objects.filter(token=token).first()
        if user_obj:
            return (user_obj,token)  # 并把返回值赋值给request.user和request.auth
        raise AuthenticationFailed("token不合法")

# 在模型
class UserInfo(models.Model):
    username = models.CharField(max_length=32)
    password = models.CharField(max_length=32)
    token = models.UUIDField(null=True,blank=True)  # 登录成功写入token的uuid

# 在视图
class AuthView(APIView):
    authentication_classes = [MyAuth,]  # 在视图中声明authentication_classes 表示只在该视图有效

    def get(self, request):
        print(request.user)
        print(request.user.username)
        print(request.auth)
        return Response('登录后发送数据')

# 在setting声明DEFAULT_AUTHENTICATION_CLASSES 表示全局范围应用认证
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES':'rest_framework.authentication.BaseAuthentication' # 对应的认证控制类
}

# 其他几种DRF框架自带的认证控制类
BasicAuthentication
SessionAuthentication
TokenAuthentication
RemoteUserAuthentication

7,权限控制组件

# 权限控制类
from rest_framework.permissions import BasePermission

class MyPermission(BasePermission):
    # 自定义的权限控制类中,必须要声明message属性和has_permission方法
    message = "错误信息" 
    def has_permission(self,request,view):   # 
        if request.user.type in [2,3]:   # 判断用户是否有权限
            return True  # 有 return True
        return False  # 没有 return False



# 在视图
class PermissionView(APIView):
    authentication_classes = [MyAuth,]
    permission_classes = [Permission,]   # 局部视图注册,只在当前视图中生效
    # 如果权限控制类返回True,程序继续执行
    # 返回False,则触发报错raise
    def get(self,request):
        # 这个接口只能vip或者vvip访问
        return Response('权限测试接口')



# 在源码
    def check_permissions(self, request):
        for permission in self.get_permissions():  # 获取权限控制类的实例化对象
            if not permission.has_permission(request, self):  #  需要实现has_permission,否则走父类的方法抛出异常
                self.permission_denied(
                    request, message=getattr(permission, 'message', None)  # 需要声明message属性,否则程序无法继续执行,报错
                ) 



# 在setting中全局注册权限组件
REST_FRAMEWORK = {
    # 配置全局权限
    "DEFAULT_PERMISSION_CLASSES": ["rest_framework.permissions.BasePermission",]
}



# DRF框架中自带的权限控制类
AllowAny
IsAuthenticated
IsAdminUser
IsAuthenticatedOrReadOnly

DjangoModelPermissions 的子类:
    DjangoModelPermissionsOrAnonReadOnly
    DjangoObjectPermissions

   

原文地址:https://www.cnblogs.com/lianyeah/p/10129185.html