极大简化Django开发,让程序员没代码敲的DRF框架

前置基础知识

web开发的两种模式

  • 前后端不分离
  • 前后端分离
  • 1. 前后端不分离

    极大简化Django开发,让程序员没代码敲的DRF框架

    完整的html页面是在后端生成的,后端给前端返回完整的页面,前端只是进行展示,前端与后端的耦合度很高。

    缺点:只适用于纯网页的应用。

    优点:有利于网站的SEO优化

    2. 前后端分离

    极大简化Django开发,让程序员没代码敲的DRF框架

    完整的html页面是在前端生成的,后端只给前端返回所需的数据,前端将数据填充在页面上,前端与后端的耦合度很低。

    优点:可以对接不同类型的客户端。

    缺点:不利于SEO优化

    3.API: 在前后端分离开发模式中,我们通常将后端开发的每个视图都称为一个接口或者API。

    4.RESTful设计风格

    统一的接口设计方式就是普遍采用的RESTful API设计风格

    Restful风格设计-关键点

    • URL地址尽量使用名词复数,不要使用动词
    • 访问同一个URL地址,采用不同的请求方式,代表要执行不同的操作。(GET)获取 POST(新增) PUT(修改)DELETE(删除) 不常用:PATCH(修改) HEAD(只返回请求头没有请求体) OPTIONS(获取信息)
    • 过滤参数可以放在查询字符串中。常见的参数:
    ?limit=10:指定返回记录的数量?offset=10:指定返回记录的开始位置。?page=2&pagesize=100:指定第几页,以及每页的记录数。?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序。
    • 针对不同操作,服务器向用户返回不同的响应数据。一般遵循以下规范:
    1. 获取一组数据,返回一组数据2. 获取指定数据,返回指定数据3. 新增数据,返回新增的数据4. 修改数据,返回修改的数据5. 删除数据,返回空
    • 服务器返回的响应数据格式,应该尽量使用JSON
    • 响应状态码,服务器向客户端返回的状态码和提示信息,常见的状态码如下:
    200 OK - [GET/PUT]:服务器成功返回用户请求的数据201 CREATED - [POST]:用户新建数据成功。204 NO CONTENT - [DELETE]:用户删除数据成功。400 INVALID REQUEST - [POST/PUT]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。。500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。

    了解:

    1. 域名:使用专有域名
    2. 版本:将版本信息放在url地址
    3. 错误:将错误信息返回
    4. 在访问api接口时,将和接口相关的其他API接口的地址也在响应数据中返回

    使用Django开发REST API

    需求:    设计一套符合RestAPI风格的接口,提供以下5个接口:        1. 获取所有直播间数据:GET /lives/        2. 新增一本直播间数据:POST /lives/        3. 获取指定的直播间数据(根据id):GET /lives/(?P\\d+)/        4. 修改指定的直播间数据(根据id):PUT /lives/(?P\\d+)/        5. 删除指定的直播间数据(根据id):DELETE /lives/(?P 
    \\d+)/

    模型类定义

    # models.pyclass LiveInfo(models.Model):    live_id = models.IntegerField(verbose_name='直播间号')    live_streamer = models.CharField(max_length=20, verbose_name='主播名字')    live_title = models.CharField(max_length=30, verbose_name='直播间标题')    live_pop = models.IntegerField(default=0, verbose_name='人气')    live_content = models.CharField(default='未设定', max_length=20, verbose_name='直播类型')    is_delete = models.BooleanField(default=False, verbose_name='删除标记')    class Meta:        db_table = 'tb_lives'        verbose_name = '直播间'        verbose_name_plural = verbose_name    def __str__(self):        return self.live_streamer

    views.py文件中定义视图实现API接口:

    class LiveListView(View):    def get(self, request):        """获取所有直播间"""        lives = LiveInfo.objects.all()        lives_list = []        for live in lives:            lives_list.append({                'live_id': live.live_id,                'live_streamer': live.live_streamer,                'live_title': live.live_title,                'live_pop': live.live_pop,                'live_content': live.live_content            })        return JsonResponse(lives_list, safe=False)    def post(self, request):        """新增直播间"""        passclass LiveDetailView(View):    def get(self, request, pk):        """查询一间直播间信息"""        pass    def put(self, request, pk):        """修改直播间信息"""        pass    def delete(self, request, pk):        """删除直播间"""        pass

    RestAPI开发核心工作

    分析上节直播间管理的5个API接口,可以发现,在开发REST API接口时,视图中做的最主要有三件事:

    • 将请求的数据(如JSON格式)转换为模型类对象
    • 操作数据库
    • 将模型类对象转换为响应的数据(如JSON格式)

    在以上操作中,涉及到两个概念:序列化和反序列化

    序列化Serialization

    将程序中的一个数据结构类型转换为其他格式(字典、JSON、XML等),例如将Django中的模型类对象转换为字典或JSON字符串,这个转换过程我们称为序列化。

    反序列化

    将其他格式(字典、JSON、XML等)转换为程序中的数据,例如将JSON字符串或字典转换保存为Django中的模型类对象,这个过程我们称为反序列化。

    在开发REST API接口时,我们在视图中在做的最核心的事是:

    • 将数据库数据序列化为前端所需要的格式,并返回。
    • 将前端发送的数据反序列化保存到模型类对象,并保存到数据库中。

    Django REST framework 简介

    Django REST framework 框架是一个用于构建Web API 的强大而又灵活的工具。通常简称为DRF框架 或 REST framework。

    作用:帮助我们大大提高REST API的开发速度

    核心功能:

    • 序列化器:序列化和反序列化
    • 类视图,MiXin扩展类:简化视图代码的编写

    环境安装与使用:

    pip install djangorestframework# 注:DRF框架依赖于Django,需要先安装Django环境,再安装djangorestframework。

    在Django项目中使用DRF框架进行开发时,需要将rest_framework在INSTALLED_APPS中进行注册:

    INSTALLED_APPS = [    ...    'rest_framework',]

    序列化器

    序列化器类定义

    from rest_framework import serializersclass LiveInfoSerializer(serializers.Serializer):    id = serializers.IntegerField(label='ID', read_only=True)    live_id = serializers.IntegerField(label='直播间号')    live_streamer = serializers.CharField(max_length=20, label='主播名字', required=False)    live_title = serializers.CharField(max_length=30, label='直播间标题')    live_pop = serializers.IntegerField(default=0, label='人气', required=False)    live_content = serializers.CharField(default='未设定', max_length=20, label='直播类型', required=False)    is_delete = serializers.BooleanField(default=False, label='删除标记', required=False) 

    选项参数:

    • write_only:为True,字段只在反序列化时使用
    • read_only:为True,字段只在序列化时使用
    • required:为True,如果字段在反序列化时使用,该字段必传传入
    • default:设置序列化和反序列化操作时的默认值
    • max_length和min_length:设置字符串的最大长度和最小长度
    • max_value和min_value:设置数字的最大值和最小值

    序列化操作

    • 序列化单个对象:序列化单个对象obj时,在创建序列化器对象时,将obj传递给instance即可
    from livetest.models import LiveInfofrom livetest.serializers import LiveInfoSerializerclass LiveDetailView(View):    def get(self, request, pk):        """查询一间直播间信息"""        try:            live = LiveInfo.objects.get(pk=pk)        except LiveInfo.DoesNotExist:            return HttpResponse(status=404)        # 创建序列化器对象        serializer = LiveInfoSerializer(live)        # 返回序列化之后的数据        return JsonResponse(serializer.data)
    • 序列化多个对象:如果要被序列化的是包含多个对象的查询集QuerySet或list,在创建序列化器对象时,需要添加many=True参数
    class LiveListView(View):    def get(self, request):        """获取所有直播间"""        lives = LiveInfo.objects.all()        # 创建序列化器对象        serialize# 获取序列化之后的数据r = LiveInfoSerializer(lives, many=True)                return JsonResponse(serializer.data, safe=False)
    • 关联对象嵌套序列化

    如果在序列化对象数据时,需要将其关联的对象一并序列化,则定义序列化器类的字段时,需要在定义对应的关联对象嵌套序列化字段,比如和直播分类关联的直播间。对于嵌套关联字段,可以采用以下3种方式进行定义:

    1.PrimaryKeyRelatedField :将关联对象序列化为关联对象的主键。

    # 在LiveInfoSerializer中添加此字段Ltype = serializers.PrimaryKeyRelatedField(label='直播类型', read_only=True)或Ltype = serializers.PrimaryKeyRelatedField(label='直播类型', queryset=LiveInfo.objects.all())

    2.使用关联对象的序列化器

    Ltype = LiveInfoSerializer()

    3.StringRelatedField:将关联对象序列化为关联对象模型类__str__方法的返回值。

    Ltype = serializers.StringRelatedField(label='直播类型')

    如果和一个对象关联的对象有多个,在序列化器类中定义嵌套序列化字段时,需要多添加一个many=True参数。

    反序列化操作

    # 1. 创建序列化器对象serializer = 序列化器类(data=)# 2. 数据校验:成功返回True,失败返回Falseserializer.is_valid()# 3. 获取校验成功之后的数据serializer.validated_data# 4. 如果校验失败,获取失败的错误提示信息serializer.errors

    调用is_valid时,会根据对应序列化类字段是否需要传递、字段类型以及一些选项参数对data中的数据进行校验。

    在调用is_valid进行数据校验时,除了一些基本的默认验证行为,可能还需要补充一些验证行为,可以使用以下三种方法:

  • 针对指定字段添加validators选项参数添加补充验证函数
  • 在序列化器类中定义特定方法validate_针对特定字段进行补充验证
  • 在序列化器类中定义方法validate进行补充验证
  • 数据校验通过之后,可以调用序列化对象的save方法进行数据保存,save方法内部会调用对应序列化器类中的create或update方法,可以在create中实现数据新增,在update中实现数据更新。

    ModelSerializer使用

    如果序列化器类对应的是Django的某个模型类,则定义序列化器类时,可以直接继承于ModelSerializer。

    ModelSerializer是Serializer类的子类,相对于Serializer,提供了以下功能:

    • 基于模型类字段自动生成序列化器类的字段
    • 包含默认的create()和update()方法的实现
    class LiveInfoSerializer(serializers.ModelSerializer):    class Meta:        model = LiveInfo# 使用fields来指明依据模型类的哪些字段生成序列化器类的字段,__all__表明包含所有字段,也可以指明具体哪些字段# fields = '__all__'        # fields = ('id', 'title', ...)        # 使用exclude可以指明排除哪些字段        exclude = ['is_delete']        extra_kwargs = {            # 'live_title': {'validators': [live_name]},            'live_streamer': {'required': False},            'live_pop': {'min_value': 0, 'required': False},        }

    案例-序列化器改写后的视图代码:

    import jsonfrom django.http import JsonResponse, HttpResponsefrom django.views import Viewfrom livetest.models import LiveInfofrom livetest.serializers import LiveInfoSerializerclass LiveListView(View):    def get(self, request):        """获取所有直播间"""        lives = LiveInfo.objects.all()        serializer = LiveInfoSerializer(lives, many=True)        return JsonResponse(serializer.data, safe=False)    def post(self, request):        """新增直播间"""        json_dict = json.dumps(request.body.decode())        serializer = LiveInfoSerializer(data=json_dict)        serializer.is_valid(raise_exception=True)        serializer.save()        # 返回新增直播间        return JsonResponse(serializer.data, status=201)class LiveDetailView(View):    def get(self, request, pk):        """查询一间直播间信息"""        try:            live = LiveInfo.objects.get(pk=pk)        except LiveInfo.DoesNotExist:            return HttpResponse(status=404)        serializer = LiveInfoSerializer(live)        return JsonResponse(serializer.data)    def put(self, request, pk):        """修改直播间信息"""        try:            live = LiveInfo.objects.get(pk=pk)        except LiveInfo.DoesNotExist:            return HttpResponse(status=404)        json_dict = json.dumps(request.body.decode())        serializer = LiveInfoSerializer(live, data=json_dict)        serializer.is_valid(raise_exception=True)        serializer.save()        return JsonResponse(serializer.data)    def delete(self, request, pk):        """删除直播间"""        try:            live = LiveInfo.objects.get(pk=pk)        except LiveInfo.DoesNotExist:            return HttpResponse(status=404)        # 参数校验        live.delete()        return HttpResponse(status=204) 

    视图部分

    APIView

    APIView是REST framework提供的所有视图的基类,继承自Django的View类。

    APIView和View的区别:

    • 请求对象:Request类的对象request.data:解析之后的请求体数据 ,request.query_params:解析之后的查询字符串数据
    • 响应对象:Response类对象统一返回Response对象,原始响应数据根据客户端请求头Accept转换为对应的格式进行返回
    • 异常处理机制默认异常处理机制,会将视图中出现的异常处理成合适的响应返回给客户端
    • 其他:认证&权限&限流

    案例-使用APIView改写RestAPI:

    GenericAPIView

    继承自APIView,在APIView功能基础上,主要增加了操作序列化器和数据库查询的属性和方法

    GenericAPIView和APIView的区别:

    • 继承自APIView
    • 封装了操作序列化器的属性和方法属性:serializer_class方法:get_serializer_class和get_serializer
    • 封装了数据库操作的属性和方法属性:queryset方法:get_queryset和get_object
    • 其他:过滤和分页

    案例-使用GenericAPIView改写RestAPI:

    from rest_framework import statusfrom rest_framework.generics import GenericAPIViewfrom rest_framework.response import Responsefrom livetest.models import LiveInfofrom livetest.serializers import LiveInfoSerializerclass LiveListView(GenericAPIView):    serializer_class = LiveInfoSerializer    queryset = LiveInfo.objects.all()    def get(self, request):        """获取所有直播间"""        queryset = self.get_queryset()        serializer = self.get_serializer(queryset, many=True)        return Response(serializer.data)    def post(self, request):        """新增直播间"""        serializer = self.get_serializer(data=request.data)        serializer.is_valid(raise_exception=True)        serializer.save()        # 返回新增直播间        return Response(serializer.data, status=status.HTTP_201_CREATED)class LiveDetailView(GenericAPIView):    serializer_class = LiveInfoSerializer    queryset = LiveInfo.objects.all()    def get(self, request, pk):        """查询一间直播间信息"""        instance = self.get_object()        serializer = self.get_serializer(instance)        return Response(serializer.data)    def put(self, request, pk):        """修改直播间信息"""        instance = self.get_object()        serializer = self.get_serializer(instance, data=request.data)        serializer.is_valid(raise_exception=True)        serializer.save()        return Response(serializer.data)    def delete(self, request, pk):        """删除直播间"""        instance = self.get_object()        # 参数校验        instance.delete()        return Response(status=status.HTTP_204_NO_CONTENT)

    Mixin扩展类

    使用GenericAPIView改写之后的RestAPI接口中,图书管理的各个代码已经变成了通用的代码,这些代码和视图所使用的序列化器类和查询集已经没有直接的关系,DRF框架已经将这些代码做了封装,就是5个Mixin扩展类。

    极大简化Django开发,让程序员没代码敲的DRF框架

    案例-Mixin改写RestAPI接口:

    from rest_framework import mixinsfrom rest_framework.generics import GenericAPIViewfrom livetest.models import LiveInfofrom livetest.serializers import LiveInfoSerializerclass LiveListView(mixins.ListModelMixin, mixins.CreateModelMixin,GenericAPIView):    serializer_class = LiveInfoSerializer    queryset = LiveInfo.objects.all()    def get(self, request):        """获取所有直播间"""        return self.list(request)    def post(self, request):        """新增直播间"""        return self.create(request)class LiveDetailView(mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, GenericAPIView):    serializer_class = LiveInfoSerializer    queryset = LiveInfo.objects.all()    def get(self, request, pk):        """查询一间直播间信息"""        return self.retrieve(request, pk)    def put(self, request, pk):        """修改直播间信息"""        return self.update(request, pk)    def delete(self, request, pk):        """删除直播间"""        return self.destroy(request, pk)

    子类视图:Django框架为了方便视图的编写,还提供了9个子类视图类。

    子类视图一定同时继承了GenericAPIView和对应的Mixin扩展类,同时类中提供了对应的请求处理方法,并且请求处理方法中调用的就是Mixin扩展类中封装的通用方法。

    极大简化Django开发,让程序员没代码敲的DRF框架

    案例-子类视图改写RestAPI接口:

    from rest_framework.generics import ListCreateAPIView, RetrieveUpdateDestroyAPIViewfrom livetest.models import LiveInfofrom livetest.serializers import LiveInfoSerializerclass LiveListView(ListCreateAPIView):    serializer_class = LiveInfoSerializer    queryset = LiveInfo.objects.all()class LiveDetailView(RetrieveUpdateDestroyAPIView):    serializer_class = LiveInfoSerializer    queryset = LiveInfo.objects.all()

    视图集ViewSet

    将操作同一组资源相关的处理方法放在同一个类中,这个类叫做视图集。

    基本使用:

  • 继承视图集父类ViewSet
  • 视图集中的处理方法不再以请求方法(get等)命名,而是以对应的action操作命名
  • url地址配置时需要明确指明请求方式和处理函数之间的对应关系
  • 常用视图集父类:

    • ViewSet:继承自APIView与ViewSetMixin,作用也与APIView基本类似,提供了身份认证、权限校验、流量管理等。
    • GenericViewSet:继承自GenericAPIView与ViewSetMixin,可以直接搭配Mixin扩展类使用。
    • ModelViewSet:继承自GenericViewSet和5个Mixin扩展类。
    • ReadOnlyModelViewSet:继承自GenericViewSet,同时包括了ListModelMixin、RetrieveModelMixin。

    案例-ViewSet改写:

    from django.http import Http404from rest_framework import statusfrom rest_framework.response import Responsefrom rest_framework.viewsets import ViewSetfrom livetest.models import LiveInfofrom livetest.serializers import LiveInfoSerializerclass LiveInfoViewSet(ViewSet):    def list(self, request):        """获取所有直播间"""        lives = LiveInfo.objects.all()        serializer = LiveInfoSerializer(lives, many=True)        return Response(serializer.data)    def create(self, request):        """新增直播间"""        serializer = LiveInfoSerializer(data=request.data)        serializer.is_valid(raise_exception=True)        serializer.save()        # 返回新增直播间        return Response(serializer.data, status=status.HTTP_201_CREATED)    def retrieve(self, request, pk):        """查询一间直播间信息"""        try:            live = LiveInfo.objects.get(pk=pk)        except LiveInfo.DoesNotExist:            raise Http404        serializer = LiveInfoSerializer(live)        return Response(serializer.data)    def update(self, request, pk):        """修改直播间信息"""        try:            live = LiveInfo.objects.get(pk=pk)        except LiveInfo.DoesNotExist:            raise Http404        serializer = LiveInfoSerializer(live, data=request.data)        serializer.is_valid(raise_exception=True)        serializer.save()        return Response(serializer.data)    def destroy(self, request, pk):        """删除直播间"""        try:            live = LiveInfo.objects.get(pk=pk)        except LiveInfo.DoesNotExist:            raise Http404        # 参数校验        live.delete()        return Response(status=status.HTTP_204_NO_CONTENT)# urls.pyfrom django.conf.urls import urlfrom booktest import viewsurlpatterns = [    url(r'^lives/$', views.LiveInfoViewSet.as_view({        'get': 'list',        'post': 'create'    })),    url(r'^lives/(?P\\d+)/$', views.LiveInfoViewSet.as_view({        'get': 'retrieve',        'put': 'update',        'delete': 'destroy'    }))]

    案例-GenericViewSet改写:

    案例-ModelViewSet并添加了一些额外的方法改写:

    from rest_framework.decorators import actionfrom rest_framework.response import Responsefrom rest_framework.viewsets import ModelViewSetfrom livetest.models import LiveInfofrom livetest.serializers import LiveInfoSerializerclass LiveInfoView(ModelViewSet):    serializer_class = LiveInfoSerializer    queryset = LiveInfo.objects.all()    lookup_value_regex = '\\d+'    @action(methods='get', detail=False)    def latest(self, request):        """查询最新的直播间"""        live = LiveInfo.objects.latest('pk')        serializer = self.get_serializer(live)        return Response(serializer.data)    @action(methods='put', detail=True)    def change_pop(self, request, pk):        """修改指定直播间的人气"""        live = self.get_object()        live.bread = request.data.get('live_pop')        live.save()        serializer = self.get_serializer(live)        return Response(serializer.data)# urls.pyfrom django.conf.urls import urlfrom . import viewsurlpatterns = [    url(r'^lives/$', views.LiveInfoViewSet.as_view({        'get': 'list',        'post': 'create'    })),    url(r'^lives/(?P\\d+)/$', views.LiveInfoViewSet.as_view({        'get': 'retrieve',        'put': 'update',        'delete': 'destroy'    })),    url(r'^lives/latest/$', views.LiveInfoViewSet.as_view({        'get': 'latest'    })),    url(r'^lives/(?P\\d+)/change_pop/$', views.LiveInfoViewSet.as_view({        'put': 'change_pop'    }))]

    路由Router:动态生成视图集中处理方法的url配置项

    对于视图集ViewSet,除了可以自己手动进行URL配置指明请求方式与action处理函数之间的对应关系外,还可以使用路由Router来自动生成路由信息。

    REST framework提供了两个Router类:

    • SimpleRouter
    • DefaultRouter

    案例-router改写:

    # urls.pyfrom rest_framework.routers import DefaultRouterfrom livetest import viewsurlpatterns = []# 创建Router对象router = DefaultRouter()# 注册routerrouter.register('lives', views.LiveInfoView, base_name='live')# 添加配置项urlpatterns += router.urls
  • lookup_value_regex: 设置router生成路由时,从url中提取pk参数对应的正则表达式
  • 视图集中额外处理方法的配置项,需要添加action装饰器
  • DefaultRouter和SimpleRouter的区别多生成一个根路径(/)配置项,并且每个配置项地址后都可以跟上.json,直接返回json数据。
  • DRF框架到此常用的功能都已经讲解完毕了,当然DRF框架还有其他功能:认证、权限、限流、过滤、排序、分页和异常处理机制。将在后面讲述,敬请关注。

    作者简介:Python菜鸟工程师,将在接下来的一段时间内与大家分享一些与Python相关的知识点。如若文中出现问题,各位大佬多多指点,互相学习。喜欢的关注一个吧!谢谢!


    分享到:


    相關文章: