学习Django后你还不了解的DRF框架(一):缩短我们编写API的时间

一、Django Rest Framework框架简介

在这里插入图片描述

该框架的核心思想就是缩短了我们编写API接口的代码量。

Django REST framework是一个建立在Django基础之上的Web 应用开发框架,可以快速的开发REST API接口应用。在REST framework中,提供了序列化器Serialzier的定义,可以帮助我们简化序列化与反序列化的过程,不仅如此,还提供丰富的类视图、扩展类、视图集来简化视图的编写工作。REST framework还提供了认证、权限、限流、过滤、分页、接口文档等功能支持。REST framework提供了一个API 的Web可视化界面来方便查看测试接口。

官方文档:https://www.django-rest-framework.org/

特点:

  • 提供了定义序列化器Serializer的方法,可以快速根据 Django ORM 或者其它库自动序列化/反序列化;
  • 提供了丰富的类视图、Mixin扩展类,简化视图的编写;
  • 丰富的定制层级:函数视图、类视图、视图集合到自动生成 API,满足各种需要;
  • 多种身份认证和权限认证方式的支持;
  • 内置了限流系统;
  • 直观的 API web 界面;
  • 可扩展性,插件丰富

二、DRF的安装及配置


2.1 安装

官方文档说明

REST框架要求以下内容:

  • Python(3.5、3.6、3.7、3.8、3.9)
  • Django(2.2、3.0、3.1)

我们强烈建议并仅正式支持每个Python和Django系列的最新修补程序版本。

以下软件包是可选的:

  • PyYAML,uritemplate(5.1+,3.0.0+)-模式生成支持。
  • Markdown(3.0.0+)-对可浏览API的Markdown支持。
  • Pygments(2.4.0+)-在Markdown处理中添加语法高亮显示。
  • django-filter(1.0.1+)-过滤支持。
  • django-guardian(1.1.1+)-对象级别权限支持。

使用pip安装:

pip install djangorestframework # DRF包

# 可选
pip install markdown       # Markdown support for the browsable API.
pip install django-filter  # Filtering support

或者使用Pycharm给使用的解释器安装:djangorestframework即可


2.2 配置

安装完成DRF以后,我们需要添加rest_framework到配置文件的INSTALLED_APPS里面。

INSTALLED_APPS = [
    ...
    'rest_framework',
]

接下来就可以使用DRF提供的功能进行api接口开发了。在项目中如果使用DRF框架实现API接口,主要有以下三个步骤:

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

接下来我们先简单体验一下DRF框架能够给我们带来什么优势


三、DRF框架的简单应用

我们这里先简单了解体验一番即可,后续再讲解代码的作用。


3.1 创建模型

便于我们能够更好演示出DRF的优势

class Book(models.Model):
    name = models.CharField(max_length=16)
    price = models.FloatField()

进行数据库迁移操作。执行以下命令:python3 manage.py makemigrationspython3 manage.py migrate

然后我们需要在里面插入几条数据
在这里插入图片描述

3.2 创建序列化容器

在syudents应用目录中新建serializers.py用于保存该应用的序列化器。

创建一个BookModelSerializer用于序列化与反序列化。

from rest_framework import serializers
from app01 import models

class BookModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Book
        fields = '__all__'

model:指明该序列化器处理的数据字段来自模型类Book
fields:指明该序列化器包含模型类中的哪些字段,all指明包含所有字段


3.3 编写视图

在app应用中的views.py文件创建视图:BookView,这里继承的方法是一个视图集;

from rest_framework.viewsets import ModelViewSet
from app01 import models
from app01 import serializer

class BookView(ModelViewSet):
    serializer_class = serializer.BookModelSerializer
    queryset = models.Book.objects.all()

serializer_class:指明该视图在进行序列化或反序列化时使用的序列化器
queryset:指明该视图集在查询数据时使用的查询集


3.4 定义路由

在urls.py文件内路由信息

from rest_framework.routers import SimpleRouter
from app01 import views

router = SimpleRouter() # 可以处理视图的路由器
router.register('book',views.BookView) # 向路由器中注册视图集

urlpatterns = []

urlpatterns += router.urls # 将路由器中的所以路由信息追到到django的路由列表中

3.5 运行测试

启动Django来运行我们刚才编写的项目

python3 manage.py runserver 127.0.0.1:8080

1、在浏览器中输入网址127.0.0.1:8080/book/,可以看到DRF提供的API Web浏览页面:
在这里插入图片描述
这种形式就等同于我们对这个Book表进行一次all查询

2、在页面底下表单部分填写书籍信息,可以访问添加新书籍的接口,保存书籍信息:
在这里插入图片描述

3、当我们发送POST请求新增一条数据后,展示的内容:而新增的数据也会被保存到Book表中
在这里插入图片描述
4、在浏览器中输入网址127.0.0.1:8080/book/2/,可以访问获取单一书籍信息的接口(id为2的书籍),呈现如下页面:
在这里插入图片描述
5、点击DELTE按钮,这个接口可以访问删除书籍的接口

在这里插入图片描述
这是我们通过浏览器访问DRF提供的API接口呈现的样子,点击旁边的JSON按钮可以将展示的内容切换成纯JSON格式的数据

在这里插入图片描述
在这里插入图片描述

综合上序提供给我们的API接口,我们通过观察URL可以发现,DRF框架非常遵守REST规范。
在这里插入图片描述
通过上图可以发现,当我们进行修改数据操作时,URL并没有发送变化,而是发送了一个PUT请求来更改数据的操作。

根据不同请求来达到不同的操作,上面简单的操作已经完成了5个接口:

  1. 查询所有数据
  2. 查询单条数据
  3. 新增数据
  4. 修改某条数据
  5. 删除某条数据


四、序列化


补充内容:我们通过POSTMAN访问DRF提供的接口得到的是JSON格式数据
在这里插入图片描述

api接口开发,最核心最常见的一个过程就是序列化,所谓序列化就是把数据转换格式,序列化可以分两个阶段:

序列化: 把我们识别的数据转换成指定的格式提供给别人。

例如:我们在django中获取到的数据默认是模型对象,但是模型对象数据无法直接提供给前端或别的平台使用,所以我们需要把数据进行序列化,变成字符串或者json数据,提供给别人。

反序列化:把别人提供的数据转换/还原成我们需要的格式。

例如:前端JS提供过来的json数据,对于python而言就是字符串,我们需要进行反序列化换成字典我们才能够使用。


五、DRF基本使用


5.1 APIView使用

DRF提供给了我们一种类似于VIew类的APIView类,可以让我们基于CBV模式编写视图。

from rest_framework.views import APIView
from rest_framework.response import Response


class BookView(APIView):
    def get(self, request):
        return Response(data={
            'status': 200,
            'msg': 'Hello World'
        })

urls.py路由

from django.urls import path
from app01 import views

urlpatterns = [
    path('book/',views.BookView.as_view())
]

页面效果:使用了DRF封装的Response响应类,展示的效果是DRF框架内定义的好的web页面,将数据填充上了。
在这里插入图片描述


六、APIView、Request源码分析


6.1 APIView源码分析

APIView与View源码大部分都有相似之处,因为它继承自View并且在其基础上做了一些封装处理而产生的。

我们从as_view入口下手

@classmethod
    def as_view(cls, **initkwargs):
	
		# 只观察我们熟悉的代码
        if isinstance(getattr(cls, 'queryset', None), models.query.QuerySet):
            def force_evaluation():
                raise RuntimeError(
                    'Do not evaluate the `.queryset` attribute directly, '
                    'as the result will be cached and reused between requests. '
                    'Use `.all()` or call `.get_queryset()` instead.'
                )
            cls.queryset._fetch_all = force_evaluation
		
		# 调用了父类View的as_view方法,并且传递了一些值进去(请求、有名无名分组的值)
        view = super().as_view(**initkwargs)
        view.cls = cls
        view.initkwargs = initkwargs

        # Note: session based authentication is explicitly CSRF validated,
        # all other authentication is CSRF exempt.
        return csrf_exempt(view)

调用as_view方法后,得到内部闭包函数view的内存地址,然后传递csrf_exempt函数了。

csrf_exempt会在内部调用view函数以及传递参数(请求、有名无名分组值),并且view函数将不会需要csrf_token验证。

def csrf_exempt(view_func):
    def wrapped_view(*args, **kwargs):
        return view_func(*args, **kwargs)
    wrapped_view.csrf_exempt = True
    return wraps(view_func)(wrapped_view)

调用view函数后将得到cls生成的一个对象,而这个cls则是我们在APIView里面调用父类as_view时将自己定义的类传了进去。

def view(request, *args, **kwargs):

	# **initkwargs就是我们在调用as_view时传递的内容(请求、有名无名分组的值)
    self = cls(**initkwargs)
    self.setup(request, *args, **kwargs)
    if not hasattr(self, 'request'):
        raise AttributeError(
            "%s instance has no 'request' attribute. Did you override "
            "setup() and forget to call super()?" % cls.__name__
        )
    return self.dispatch(request, *args, **kwargs)

APIView而与View源码最大的区别就是调用的dispatch方法,实例的对象是我们自己定义的类的,然后执行对象的self.dispatch方法(APIView类的),并且将请求与一些值传入。

 def dispatch(self, request, *args, **kwargs):
     self.args = args
     self.kwargs = kwargs
	
	 # 将原始request请求传入,进行一番封装,得到一个新的request请求
     request = self.initialize_request(request, *args, **kwargs)
     self.request = request
     self.headers = self.default_response_headers  # deprecate?

     try:
     	'''
     	重点!!!
     		在进行路由分发执行我们相对应得请求方法之前,在这个方法中,做了认证,权限,频率,
     		如果在这三个组件中,任意一个没有通过,请求都会被拦截下来,
     		后续我们会深入内部了解。
     	'''
         self.initial(request, *args, **kwargs)

         # 判断请求是否在self.http_method_names(包含了多个HTTP请求标识)列表内
         if request.method.lower() in self.http_method_names:

			# 获取我们定义的类里面是否有和请求名相同的方法,获取其内存地址
             handler = getattr(self, request.method.lower(),
                               self.http_method_not_allowed)
         else:
             handler = self.http_method_not_allowed
		
		# 得到执行对应请求的方法内存地址并且调用了,比如方法名是get,得到其返回的内容
         response = handler(request, *args, **kwargs)

     except Exception as exc:
         response = self.handle_exception(exc)
	
	 # 将请求、以及调用执行请求的方法返回值都进行传递
     self.response = self.finalize_response(request, response, *args, **kwargs)
     return self.response

其实self.finalize_response方法就是将我们处理请求的方法返回的值转换成JSON格式数据,将最终响应结果返回。

其中有一行就是判断我们返回的数据是否为Response类的实例:

if isinstance(response, Response):

6.2 Request对象源码分析

在上序分析APIView源码的过程中有一处提到将原生的request对象传入到了一个方法内进行了一番封装,那么再对其展开进行分析。

request = self.initialize_request(request, *args, **kwargs)

该方法执行了Request这个类,并且将请求传递给了它

def initialize_request(self, request, *args, **kwargs):
    """
    Returns the initial request object.
    """
    parser_context = self.get_parser_context(request)

    return Request(
        request,
        parsers=self.get_parsers(),
        authenticators=self.get_authenticators(),
        negotiator=self.get_content_negotiator(),
        parser_context=parser_context
    )

Request类的初始化方法(部分):

def __init__(self, request, parsers=None, authenticators=None,
             negotiator=None, parser_context=None):

    self._request = request # 获取原生request请求,赋值给对象的_request属性
    self.parsers = parsers or ()
    self.authenticators = authenticators or ()
    self.negotiator = negotiator or self._default_negotiator()
    self.parser_context = parser_context
    self._data = Empty
    self._files = Empty
    self._full_data = Empty
    self._content_type = Empty
    self._stream = Empty

其实我们使用APIView给我们封装的request请求与原生的几乎无异:
在这里插入图片描述
紫色P代表伪装成对象属性的方法,每当我们通过request.属性如果能对应上则会触发。

每当我们在继承了APIView的类里面通过request.属性只要调用的属性不存在则触发__getattr__这个魔法方法,然后去原生request里面查找,找到的话则返回值。

def __getattr__(self, attr):
   try:
       return getattr(self._request, attr)
   except AttributeError:
       return self.__getattribute__(attr)

如果本文对您有帮助,别忘一键3连,您的支持就是笔者最大的鼓励,感谢阅读!

技术小白记录学习过程,有错误或不解的地方请指出,如果这篇文章对你有所帮助请点赞 收藏+关注 子夜期待您的关注,谢谢支持!

  • 15
    点赞
  • 68
    收藏
    觉得还不错? 一键收藏
  • 12
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值