Django Rest Framework

1. Web应用开发模式

1.1 前后端分离

 

通俗地讲, 其实判定很简单:如果前端和后端这两个角色, 只通过API 文档就能进行数据交流,就说明他们的逻辑是分离的。我们可以称之为 “前后端代码分离”。

如果除了 API 文档之外还需要各种其他的数据交流方式,比如后端把数据藏在一个 div 的属性里,那么就不是前后端分离的。像各种框架里的模板引擎和渲染功能。

至于所谓的 “最佳实践”并不存在, 但通用的, 类似的方案如下:

  • 前端服务器
    • 将前端的所有代码使用javascript+node.js的方式部署在node.js服务器上,作为前端稳定的服务,提供页面使用。
  • 后端服务器
    • 后端的服务器写各种业务逻辑代码, 如业务逻辑, 鉴权逻辑.
  • 前后端交互
    • 后端服务为前端提供一个统一的访问入口(一般是单独部署一个服务),使用restful等风格提供http服务供前端调用,实现前后端数据传输(通常以JSON来交换数据)。

 

 

这样一来, 现在 前端服务器不再处理任何业务,它接收到请求后,经过转换,发送给各个相关后端服务器,将各个后端服务器返回的,处理过的业务数据填入 HTML 模板,最后发送给浏览器。前端服务器和后端服务器间,可以选用任何你觉得合适的通信手段,可以是 REST,可以是 RPC,选用什么样的通信手段,这是另一个议题了。

1.2

1.3 前后端分离, 是浏览器和后端服务分离吗?

不是,前后端分离里的前端不是浏览器,指的是生成 HTML 的那个服务,它可以是一个仅仅生成 HTML 的 Web 服务器,也可以是在浏览器中通过 JS 动态生成 HTML 的 单页应用。

 

实践中,有实力的团队往往在实现前后端分离里时,前端选用 node 服务器,后端选用 C#、Java 等。

2. API接口规范

 

Restful

  • 基于http协议
  • 传统的路由设计
    • 增: localhost.com/user/add
    • 删: localhost.com/user/delete/1
    • 改: localhost.com/user/edit/1
    • 查: localhost.com/user/query/1
  • Restful的设计规范, 基于资源, 资源操作由请求方式决定
  • 比如 api.localhost/users/
    • 增: post
    • 删: delete
      • 删除单个资源: api.localhost.com/users/1
      • 删除所有资源: localhost/users/
    • 改:
      • put: 更新单个资源
      • patch: 更新局部/部分资源
    • 查: get
      • 查单个资源: api.localhost.com/users/1
      • 查所有资源: api.localhost.com/users/
    • 过滤,通过在url上传参的形式传递搜索条件
      • https://api.localhost.com/v1/zoos?limit=10:指定返回记录的数
      • https://api.localhost.com/v1/zoos?offset=10:指定返回记录的开始位置
      • https://api.localhost.com/v1/zoos?page=2&per_page=100:指定第几页,以及每页的记录数
      • https://api.localhost.com/v1/zoos?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序
      • https://api.localhost.com/v1/zoos?animal_type_id=1:指定筛选条件

RPC

  • 性能更高
  • 基于TCP/IP协议(传输层)

3. 序列化

3.1 什么是序列化?

序列化 (Serialization)是将对象的状态信息转换为可以存储或传输的形式的过程。

  • 序列化:把对象转化为可传输的字节序列过程称为序列化。
  • 反序列化:把字节序列还原为对象的过程称为反序列化。

 

3.2 为什么要序列化?

如果光看定义我想你很难一下子理解序列化的意义,那么我们可以从另一个角度来推导出什么是序列化, 那么究竟序列化的目的是什么?

其实序列化最终的目的是为了对象可以跨平台存储,和进行网络传输。而我们进行跨平台存储和网络传输的方式就是IO,而我们的IO支持的数据格式就是字节数组

 

在大多数开发者, 所谓序列化是指数据在 对象和json之前的相互转换.

 

 

4. Django Rest Framework

4.1 DRF的安装

创建新环境

conda create –prefix=C:/ProgramData/Anaconda3/envs/ENV_DRF_01 python=3.9

– 激活环境

  • conda activate C:\ProgramData\Anaconda3\envs\ENV_DRF_01

安装django

  • conda install django=3.2.*

创建django项目

  • django-admin startproject myshop

安装drf, drf是基于django的一个包

  • pip install djangorestframework
  • pip install pymysql

创建django项目内的APP

  • python manage.py startapp LearnDRF

进行django配置

  • 数据库
  • INSTALLED_APPS

 

4.2 DRF快速体验

4.2.1 创建并配置数据库

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'learndrf',  # 数据库名字
        'USER': 'root',
        'PASSWORD': 'zxcvbnm',
        'HOST': '127.0.0.1',  # 那台机器安装了MySQL
        'PORT': 3306,
    }
}

4.2.2 模型类

class Student(models.Model):
    # 模型字段
    name = models.CharField(max_length=100,verbose_name="姓名")
    sex = models.BooleanField(default=1,verbose_name="性别")
    age = models.IntegerField(verbose_name="年龄")
    class_null = models.CharField(max_length=5,verbose_name="班级编号")
    description = models.TextField(max_length=1000,verbose_name="个性签名")

    class Meta:
        db_table="tb_student"
        verbose_name = "学生"
        verbose_name_plural = verbose_name

4.2.3 执行迁移

python manage.py makemigrations
python manage.py migrate

可以添加一些数据, 方便测试.

 

4.2.4 FBV 和CBV

4.2.4.1 FBV

fbv是指function based view, 望文生义, 义如其名.

fbv 模式的路由

path(‘login_fbv/’, login)

  • login是view.py的login函数.

4.2.4.2 CBV

path(‘student/’, StudentView.as_view())

  • StudentView是view.py的类
  • as_view()是drf包里面视图方法

 

5. DRF的使用

5.1 DRF的优势?

谈到DRF的使用, 很容易会产生一堆问题? DRF有什么作用? 通常用于哪些场景? 它的优势是什么? 为什么用DRF, 而不是django?

一句话回答: DRF可以更快, 更高效的完成REST API开发.

5.2 DRF如何实现这些优势的?

DRF包含一系列高度抽象和功能丰富的组件

5.2.0.1 序列化组件

1.作用: 把python中的对象,转成json格式字符串

使用步骤1: 写一个类继承Serializer或者ModelSerializer
举例(类中选取字段进行序列化):
class BookSerializer(serializers.Serializer):
id = serializers.CharField()
title = serializers.CharField()
price = serializers.CharField()
举例(把类中字段全部进行序列化):

class TestSer(serializers.ModelSerializer):
class Meta:
model = models.Takes
fields = ‘__all__’
总结:
变量名和source指定的值不能一样

source=’publish.name’还支持继续 .

source 还支持方法(没用)

支持写方法,如下
方法一定传一个参数,是当前book对象

publish_dic=serializers.SerializerMethodField()
def get_publish_dic(self,obj):
return
结果:{‘id’:obj.publish.pk,’name’:obj.publish.name}

5.2.0.2 认证组件

1.认证组件的好处:
比如要访问books/路径,必须登录之后才能访问。一旦登录成功,在响应结果中写一个随机字符串

举例: {status:100
msg:登录成功
token:sdafsdfasd
}

2.使用步骤1:写一个类,继承BaseAuthentication

3.使用步骤2:def authenticate(self,request) ,记住传request对象

4.在视图类中使用:(不要加括号):

1.局部使用:authentication_classes=[AuthLogin](写在views中)

2.全局使用:-REST_FRAMEWORK=
{“DEFAULT_AUTHENTICATION_CLASSES”:[“app01.auth.AuthLogin”,](在setting中配置)

3.全局使用的局部禁用:authentication_classes = [](写在views中)

 

5.2.0.3 权限组件

1.使用步骤1:写一个类,继承BasePermission

2.使用步骤2:def has_permission(self, request, view): ,记住传
request对象和view

3.在视图类中使用:(不要加括号):

1.局部使用:permission_classes=[MyPer](写在views中)

2.全局使用:-REST_FRAMEWORK={“DEFAULT_PERMISSION_CLASSES”:[‘app01.auth.MyPer’]}(在setting中配置)

3.全局使用的局部禁用:permission_classes = [](写在views中)

 

5.2.0.4 节流组件

1.使用步骤1:写一个类,继承SimpleRateThrottle

2.使用步骤2:def get_cache_key(self, request, view):,记住传request对象和view

3.使用步骤3
:’DEFAULT_THROTTLE_RATES’: {
这个key跟scope对应,value值3/m 3/h 4/d
‘xx’: ‘3/m’
}(在setting中配置)

节流作用:节流(Throttling)类似于权限,因为它决定了是否应该对请求进行授权。节流表示一个临时状态,并用于控制客户端对API的请求率。

5.2.0.5 视图组件

使用:
1.局部使用:throttle_classes = [VisitThrottle](写在views中)
2.全局使用:REST_FRAMEWORK={“DEFAULT_THROTTLE_CLASSES”:[“app01.auth.VisitThrottle”]}(在setting中配置)
3.全局使用的局部禁用:throttle_classes = [](写在views中)

视图作用: 数据库查询, 2. 构建序列化器, 进行序列化操作, 返回数据

 

5.2.0.6 解析器

1.局部使用:parser_classes=[JSONParser,](写在views中)
2.全局使用:’DEFAULT_PARSER_CLASSES’:[‘rest_framework.parsers.JSONParser’](在setting中配置)

解析器作用:解析器的作用就是服务端接收客户端传过来的数据,把数据解析成自己可以处理的数据。本质就是对请求体中的数据进行解析。

 

5.2.0.7 渲染器

DRF提供的渲染器有很多,默认是
‘DEFAULT_RENDERER_CLASSES’: (
‘rest_framework.renderers.JSONRenderer’,
‘rest_framework.renderers.BrowsableAPIRenderer’,
),

渲染器的作用:渲染器同解析器相反,它定义了框架按照content_type来返回不同的响应。

 

5.2.0.8 分页组件

1.路由

urlpatterns = [
re_path(‘(?P<version>[v1|v2]+)/page1/’, Pager1View.as_view(),) #分页1
]
序列化

from rest_framework import serializers
from api import models
class PagerSerialiser(serializers.ModelSerializer):
class Meta:
model = models.Role
fields = “__all__”
视图

from api.utils.serializsers.pager import PagerSerialiser
from rest_framework.response import Response
from rest_framework.pagination import PageNumberPagination
class Pager1View(APIView):
def get(self,request,*args,**kwargs):
#获取所有数据
roles = models.Role.objects.all()
#创建分页对象
pg = PageNumberPagination()
#获取分页的数据
page_roles = pg.paginate_queryset(queryset=roles,request=request,view=self)
#对数据进行序列化
ser = PagerSerialiser(instance=page_roles,many=True)
return Response(ser.data)
settings配置

REST_FRAMEWORK = {
#分页
“PAGE_SIZE”:2 #每页显示多少个
}

5.2.0.9 版本组件

自定义版本处理

class ParamVersion(object):
def determine_version(self, request, *args, **kwargs):
# version = request._request.GET.get(“version”)
# version = request.query_params.get(‘version’)
version = request.GET.get(“version”)
return version

class UsersView(APIView):
versioning_class = ParamVersion

def get(self, request, *args, **kwargs):
version = request.version
return JsonResponse({‘version’: version})

5.2.0.10 路由组件

使用方法

# format参数是给渲染器用的
url(r’^(?P<version>[v1|v2]+)/myview\.(?P<format>\w+)$’, MyView.as_view({“get”: “list”, “post”: “create”})),
创建router对象,并注册视图集。

from rest_framework import routers
router = routers.DefaultRouter()
router.register(r’myview’, MyView)

最原始的 Serializer + APIView

2. ModelSerializer + APIView

 

6. 序列化和视图类的使用

6.1 前置了解

视图类

  • APIView
    • 继承APIView父类(Django中View的子类)
    • 具备View的所有特性
    • 提供了认证、授权、限流等功能
  • GenericAPIView
    • GenericAPIView为APIView的子类,它拓展了过滤、查询、分页的功能

序列化器

  • Serializer
  • ModelSerializer

这里将会通过以下三种组合来熟悉DRF的写法.

DRF的 Serializer + DRF的 APIView

DRF的 ModelSerializer + DRF的 APIView

DRF的 ModelSerializer + DRF的 GenericAPIView

 

6.2 Serializer + APIView

path('student/', StudentView.as_view()), #drf的as_view方法
re_path('student/(\d+)/', StudentDetailView.as_view()), #drf的as_view方法

 

class StudentView(APIView):


    def get(self, request):
        print(request.GET)
        print(request.query_params)
        students = Student.objects.all()
        print(type(students))
        # students 是一个queryset 数据集. 所谓的序列化就是对该数据再进行封装, 这里其实是for循环, 将里面的数据都拿到,

        serializer = StudentsSerializer(instance=students, many=True)
        return Response(serializer.data)



    def post(self, request):
        print(request)
        print(request.data)
        print(type(request.data))

        serializer = StudentsSerializer(data=request.data)
        if serializer.is_valid():
            stu = Student.objects.create(**serializer.validated_data)
            ser = StudentsSerializer(instance=stu, many=False)
            return Response(ser.data)

        else:
             return Response(serializer.errors)



class StudentDetailView(APIView):

    print('StudentDetailView')
    def get(self, request, id):
        students = Student.objects.get(pk=id)
        serializer = StudentsSerializer(instance=students, many=False)
        return Response(serializer.data)


    def delete(self, request, id):
        Student.objects.get(pk=id).delete()

        return Response("已删除")

    def put(self, request, id):
        print(request)
        print(request.data)
        print(type(request.data))

        serializer = StudentsSerializer(data=request.data)
        if serializer.is_valid():
            print(serializer.validated_data)

            # 成功修改数据的条数, 为1
            # n = Student.objects.filter(pk=id).update(**serializer.validated_data)
            print(n)

            #要修改的数据对象
            stu = Student.objects.get(id=id)
            print(id)
            ser = StudentsSerializer(instance=stu, many=False)
            print(ser.data)
            return Response(ser.data)

        else:
            return Response(serializer.errors)

get 查询所有数据

  • http://127.0.0.1:8000/student/

post 添加数据

  • http://127.0.0.1:8000/student/

delete 删除单条数据

  • http://127.0.0.1:8000/student/1/

put 修改单条数据

  • http://127.0.0.1:8000/student/1/

get 查询单条数据

  • http://127.0.0.1:8000/student/1/

6.3 ModelSerializer + APIView

path('publish/', PublishView.as_view()),
re_path('publish/(\d+)/', PublishDetailView.as_view()),

 

class PublishSerializer(serializers.ModelSerializer):
    class Meta:
        model = Publish
        # fiedls = "__all__"
        fields = ["name", "email"]


class PublishView(APIView):
    def get(self, reqeust):
        publishes = Publish.objects.all()
        ps = PublishSerializer(instance=publishes, many=True)
        return Response(ps.data)

    def post(self, request):
        print(request.data, type(request.data))

        serializer = PublishSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()  # create方法 将符合条件的数据插入到数据库
            return Response(serializer.data)
        else:
            return Response(serializer.errors)


class PublishDetailView(APIView):

    def get(self, reqeust, id):
        publish = Publish.objects.get(pk=id)
        ps = PublishSerializer(instance=publish, many=False)
        return Response(ps.data)

    def delete(self, request, id):
        Publish.objects.get(pk=id).delete()
        return Response()

    def put(self, request, id):
        # post请求
        instance = Publish.objects.get(pk=id)
        serializer = PublishSerializer(data=request.data, instance=instance)

        if serializer.is_valid():
            serializer.save()  # instance的 update
            return Response(serializer.data)

        else:
            return Response(serializer.errors)

 

 

6.4 ModelSerializer + GenericAPIView

path('author/', AuthorView.as_view()),
re_path('author/(?P<pk>\d+)/', AuthorDetailView.as_view()),

 

from rest_framework.generics import GenericAPIView


class AuthorSerializer(serializers.ModelSerializer):
    class Meta:
        model = Author
        fields = ["name", "age"]


class AuthorView(GenericAPIView):
    queryset = Author.objects.all()
    serializer_class = AuthorSerializer

    def get(self, reqeust):
        # serializer = self.serializer_class(instance=self.queryset,many=True)
        serializer = self.get_serializer(instance=self.get_queryset(), many=True)

        return Response(serializer.data)

    def post(self, request):

        serializer = self.get_serializer(data=request.data)

        if serializer.is_valid():
            serializer.save()  # create方法 将符合条件的数据插入到数据库
            return Response(serializer.data)
        else:

            return Response(serializer.errors)


class AuthorDetailView(GenericAPIView):
    queryset = Author.objects.all()
    serializer_class = AuthorSerializer

    def get(self, request, pk):  # url的pk名
        serializer = self.get_serializer(instance=self.get_object(), many=False)
        return Response(serializer.data)

    def delete(self, request, pk):
        self.get_object().delete()
        return Response()

    def put(self, request, id):
        # post请求
        instance = self.get_object()
        serializer = self.get_serializer(data=request.data, instance=instance)

        if serializer.is_valid():
            serializer.save()  # instance的 update
            return Response(serializer.data)

        else:
            return Response(serializer.errors)

 

6.5 ModelSerializer + Mixin

from rest_framework.mixins import ListModelMixin, CreateModelMixin, RetrieveModelMixin, DestroyModelMixin, \
    UpdateModelMixin

from rest_framework.generics import GenericAPIView


class AuthorSerializer(serializers.ModelSerializer):
    class Meta:
        model = Author
        fields = ["name", "age"]


class AuthorView(GenericAPIView, ListModelMixin, CreateModelMixin):
    queryset = Author.objects.all()
    serializer_class = AuthorSerializer

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

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


class AuthorDetailView(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
    queryset = Author.objects.all()
    serializer_class = AuthorSerializer

    def get(self, request, pk):  # url的pk名
        return self.retrieve(request)

    def delete(self, request, pk):
        return self.destroy(request)

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

6.6 基于Mixin的进一步封装

ListModelMixin, CreateModelMixin, RetrieveModelMixin, DestroyModelMixin, \ UpdateModelMixin

 

from rest_framework.generics import ListCreateAPIView,RetrieveUpdateDestroyAPIView


class AuthorSerializer(serializers.ModelSerializer):
    class Meta:
        model = Author
        fields = ["id","name", "age"]


class AuthorView(ListCreateAPIView):
    queryset = Author.objects.all()
    serializer_class = AuthorSerializer


class AuthorDetailView(RetrieveUpdateDestroyAPIView):
    queryset = Author.objects.all()
    serializer_class = AuthorSerializer

 

本站文章除单独注明外均为原创,本文链接https://bowmanjin.com/670,未经允许请勿转载。

0

评论0

请先

没有账号? 注册  忘记密码?