一、基本概念

DRF(Django Rest Framework)是可以快速基于Restful开发得Django应用得插件,功能非常多,被广泛应用。
所谓Restful风格就是不在用户请求的URL当中说明 “操作动作(create,delete,put)”。而是直接请求资源,通过不同的http方法来做对应的操作。
比如:

GET 127.0.0.1:8000/app1/ 获取app1应用的所有资源POST 127.0.0.1:8000/app1/新增app1响应的数据GET 127.0.0.1:8000/app1/1 获取app1应用下主键为 1 的资源PUT 127.0.0.1:8000/app1/1 修改app1应用下主键为 1 的资源delete 127.0.0.1:8000/app1/1删除app1应用下主键为 1 的资源

二、安装

pip install djangorestframework

注册:
在settings.py文件中进行注册.注册之后呢,在使用浏览器访问的时候,返回的结果会渲染成html页面

INSTALLED_APPS = [......'rest_framework',]

三、DRF的视图类

django的视图类: View
DRF的视图类: APIView
View是APIView的父类,所以这两个视图类的用法是一样的

1.APIView

1.1 创建应用

django-admin startapp app1

1.2 配置总路由

编辑项目目录下的总urls.py文件

urlpatterns = [path('app1/',include('app1.urls')),]

1.3 新建分布式路文件

在app1应用目录下新建urls.py文件,内容如下

from django.urls importpathfrom .views import *urlpatterns = [path('',test.as_view()),]

1.4 新建视图函数

from rest_framework.views importAPIView,Responseclass test(APIView):def get(self,request):returnResponse({"message": "访问GET方法成功"})def post(self,request):returnResponse({"message": "访问POST方法成功"})

1.5 验证

分别使用GET方法和POST方法请求以下连接

http://127.0.0.1:8000/app1/

2.APIView GET请求

request.query_params: 获取GET请求所有参数request.query_params.get("name") :获取get请求中某个key的值

视图函数:

from rest_framework.views importAPIView,Responseclass test(APIView):def get(self,request):print(request.query_params)print(request.query_params.get("name"))returnResponse({"message": "访问GET方法成功"})

发送请求验证

http://127.0.0.1:8000/app1/?name=zhangsan&password=123 

结果如下:

<QueryDict: {'name': ['zhangsan'], 'password': ['123']}> zhangsan 

3.APIView POST请求

request.data.get() : 获取post请求的数据

视图函数如下:

from rest_framework.views importAPIView,Responseclass test(APIView):def post(self,request):print(f'POST的数据 = {request.data.get("user")}')print(f'POST的数据 = {request.data.get("password")}')returnResponse({"message": "访问POST方法成功"})

发送POST请求

http://127.0.0.1:8000/app1/form-data 如下:user:adminpassword: 123123

结果如下:

POST的数据 = admin POST的数据 = 123123 

4.DRF的Response

DRF封装了自己的Response,对django的Httpsponse进行了封装和增强,可以直接对字典进行序列化。代码如下:

from rest_framework.response import Responsedata = {"usernmae": "张三","age": 100}class test(APIView):def get(self,request):returnResponse(data)

使用get请求访问

http://127.0.0.1:8000/app1/

返回结果如下:

{"usernmae": "张三","age": 100}

四、DRF序列化

1.概念

1.1 序列化

将内存中对象存储下来,把它变成一个个字节。
简单来讲: 数据结构–>二进制
举例:python中的json.dumps()就是序列化函数。将python中的字典转换成json格式的字符串的过程就是序列化

1.2.反序列化

将文件得一个个字节恢复成内存中得对象
简单来讲: 二进制–>数据结构
举例:python中的json.loads()就是反序列化,将json字符串转换成python的中字典的过程称为反序列化。

1.3 DRF序列化

在DRF中:model类–>字典–>JSON字符串(字符序列)
django中得DRF主要是配置model类来使用得。他主要是将”model类得实例” 转化为 “字典”。再由json模块转换为json字符串.

1.4 DRF反序列化

在DRF中:JSON字符串–>字典–>model类(入库持久化)
浏览器提交过来得数据,DRF做数据校验,在入库

2.序列化器

1.创建数据库model类

!!! 注意 !!!:这里定义模型类的时候,不要定义id字段,django在迁移的时候,会自动生成id字段,并且是primary_key 和自增长的字段。
如果写了,在后边写PUT方法的时候,会有一个报错:如果POST的数据中不传递id 会报错,说ID是必填字段。如果填写了又说ID是存在的。

在应用app1的models.py中编写mode类

from django.db import modelsclass student(models.Model):class Meta:db_table = 'my_student'name = models.CharField(max_length=20)age = models.IntegerField()

进行迁移

python manage.py makemigrationspython manage.py migrate

查看生成的表结构

mysql> desc my_student;+-------+-------------+------+-----+---------+----------------+| Field | Type| Null | Key | Default | Extra|+-------+-------------+------+-----+---------+----------------+| id| bigint(20)| NO | PRI | NULL| auto_increment || name| varchar(20) | NO | | NULL||| age | int(11) | NO | | NULL||+-------+-------------+------+-----+---------+----------------+

2.插入内容

INSERT INTO my_student VALUES (0,'zhangsan',20),(0,'lisi',21),(0,'wangwu',20),(0,'xiaoqiang',26),(0,'xiaoming',22);mysql> select * from my_student;+----+-----------+-----+| id | name| age |+----+-----------+-----+|1 | zhangsan|20 ||2 | lisi|21 ||3 | wangwu|20 ||4 | xiaoqiang |26 ||5 | xiaoming|22 |+----+-----------+-----+5 rows in set (0.00 sec

3.创建序列化器

在app1的应用目录下创建serializers.py文件.文件名是可以自定义的。
DRF提供了一个类:serializers.ModelSerializer。这个类就帮我们做了序列化器和model字段的一一对应
这一步创建序列化器才是在序列化过程中最重要的的步骤。

from .models import *from rest_framework import serializersclass student_serializers(serializers.ModelSerializer):class Meta:# 这是对student 类进行序列化model = student# 序列化所有字段fields = '__all__'

3.序列化

注意:单行序列化 获取数据库数据时使用的是objects.get()
多行序列化 获取数据库数据时使用的是objects.filter(),并且做数据转换的时候必须使用many=True参数。

编写路由

编辑app1应用下的路由文件urls.py。
这里使用了两个函数test和testDetail

from django.urls importpathfrom .views import *urlpatterns = [# 这个路由是为了符合restful风格的接口,get所有资源准备的路由.path('',test.as_view()),# 这个路由是为了对单个资源进行操作的路由.# 在路由中也定义了 用来定位资源的变量: idpath('/',testDetail.as_view())]

1 单行序列化

编写app1应用下视图函数文件views.py

from rest_framework.views importAPIView,Responsefrom .models import *from .serializers import *class testDetail(APIView):def get(self, request,id):# 获取单行数据库数据res = student.objects.get(pk=id)# 使用自定义序列化器 对结果进行序列化,并且使用.data返回序列化后的数据ser_data = student_serializers(instance=res).data# 将最后的结果序列化为json返回给用户return Response(ser_data)

用户使用GET请求

http://127.0.0.1:8000/app1/2

结果如下:

{"id": 2,"name": "lisi","age": 21}

2 多行序列化

依然是app1下视图函数文件views.py文件

from rest_framework.views importAPIView,Responsefrom .models import *from .serializers import *# 这个函数和上边不是一个函数class test(APIView):def get(self,request):# 获取数据库 符合条件的多行数据集。这里的参数应该是用户传递过来的,这里就直接写了res = student.objects.filter(age__gt=20)# 对多行数据集合进行序列化ser_data = student_serializers(instance=res,many=True).data# 将数据序列化后返回给用户returnResponse(ser_data)

结果如下:

[{"id": 2,"name": "lisi","age": 21},{"id": 4,"name": "xiaoqiang","age": 26},{"id": 5,"name": "xiaoming","age": 22}]

4、反序列化

1.新增数据(POST)

视图函数如下:

from rest_framework.views importAPIView,Responsefrom .models import *from .serializers import *class test(APIView):def get()......# 新增POST方法def post(self,request):# 将用户post的请求的数据传递给序列化器ser_data = student_serializers(data=request.data)# 序列化器对 用户提交的数据进行合法校验if ser_data.is_valid():ser_data.save()returnResponse("数据写入成功")

用户访问路由,使用POST方法:

http://127.0.0.1:8000/app1/用户POST数据为:{"name": "zhangwuji","age": 20}

查看数据库,数据已经添加成功

mysql> select * from my_student;+----+-----------+-----+| id | name| age |+----+-----------+-----+|1 | zhangsan|20 ||2 | lisi|21 ||3 | wangwu|20 ||4 | xiaoqiang |26 ||5 | xiaoming|22 ||6 | zhangwuji |20 |+----+-----------+-----+6 rows in set (0.00 sec)

2.更新数据(PUT)

class testDetail(APIView):def get(self,request,id):pass# 新增put方法def put(self,request,id):query_data = student.objects.get(pk=id)ser_save_data = student_serializers(instance=query_data, data=request.data)if ser_save_data.is_valid():ser_save_data.save()returnResponse("数据修改成功")else:print(ser_save_data.errors)returnResponse("数据修改失败")

使用PUT方法访问:

PUT http://127.0.0.1:8000/app1/6/

将zhangwuji改为zhangsanfeng,提交数据为:

{"name": "zhangsanfeng","age": 20}

查看结果修改成功

mysql> select * from my_student;+----+--------------+-----+| id | name | age |+----+--------------+-----+|1 | zhangsan |20 ||2 | lisi |21 ||3 | wangwu |20 ||4 | xiaoqiang|26 ||5 | xiaoming |22 ||6 | zhangsanfeng |20 |+----+--------------+-----+6 rows in set (0.00 sec)

3.删除数据

class testDetail(APIView):def get(self,request,id):passdef put(self,request,id):pass# 新增delete方法def delete(self,request,id):try:query_data = student.objects.get(pk=id)query_data.delete()except Exception:return Response("数据删除失败")else:return Response("数据删除成功")

使用delete方法请求.无需post数据。
删除id为 6 的数据

http://127.0.0.1:8000/app1/6/

5.总结

这里一共两个函数,5个方法。增删改查查

class test(APIView):# 获取所有资源def get()# 添加一个资源def post()class testDetail(APIView):# 获取单个资源def get()# 修改单个资源def put()#删除单个资源def delete()

如下图:

五、通用视图类

1.视图类的缺点

使用了APIview确实有些了增强的部分,但是如果有多个应用,或者一个应用里多个业务类,那就会有很多的重复代码。
重复代码指的是:
1.每一个业务类当中都要重复写get、post方法
2.每一个方法当中都要重复写 查询集合,序列化器、数据校验、数据保存等
于是:DRF又提供了genericAPIview和mixins

2.GenericAPIView

GenericAPIView主要是提供了两个变量,这两个变量的名称是固定写法。因为源码里是这么写的。这两个变量主要是提供给Mixins类使用的。

from rest_framework.generics import GenericAPIView1.queryset: 得出查询数据库数据的集合2.serializer_class:指定序列化器

3.mixins类

在View和APIView中,我们都需要手写了get、put、post等方法,来处理增删改查查的数据。
DRF中提供了这5个类,和get、put、delete、get、post的功能一样,而且比自己写的处理数据逻辑更严谨

ListModelMixin: 查所有RetrieveModelMixin:查单条CreateModelMixin:新增UpdateModelMixin:更新DestroyModelMixin:删除

4.组合使用

4.1 总路由

urlpatterns = [path('app1/',include('app1.urls')),]

4.2 子路由

from django.urls importpathfrom .views import *urlpatterns = [path('',test.as_view()),# 这里的变量名必须叫做pk. 前边的视图函数可以自定义,参数也可以自定义,所以这里的路由变量也可以自定义。# 但是现在用的是GenericAPIView类,源码当中定义的叫做pk,所以这里必须叫做pkpath('',testDetail.as_view())]

4.3 model类和序列化器

model类和序列化器没有变化,依然使用上边”四”中的配置即可

4.4 视图函数

编辑views.py文件

from rest_framework.generics import GenericAPIViewfrom rest_framework.mixins import (ListModelMixin,RetrieveModelMixin,CreateModelMixin,UpdateModelMixin,DestroyModelMixin)from .models import *from .serializers import *class test(GenericAPIView,ListModelMixin,CreateModelMixin):queryset = student.objects.all()serializer_class = student_serializers# 这里之所以要这样赋值,根本原因是APIView和View都是通过dispatch方法,根据方法的名称进行的请求分发。# 所以DRF提供的5个处理数据的方法,最终名称还是要和put、post等5个方法的名称对应上。否则dispatch方法无法找到视图类get = ListModelMixin.listpost = CreateModelMixin.createclass testDetail(GenericAPIView,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin):queryset = student.objects.all()serializer_class = student_serializersget = RetrieveModelMixin.retrieveput = UpdateModelMixin.updatedelete = DestroyModelMixin.destroy

4.5 测试

DRF默认不支持批量新增和批量修改

1.get所有资源 http://127.0.0.1:8000/app1/2.get单个资源 http://127.0.0.1:8000/app1/23.delete单个数据http://127.0.0.1:8000/app1/94.post单个数据 http://127.0.0.1:8000/app1/ 数据如下:{"name": "zhangsanfeng","age": 20}5.put 单个数据 http://127.0.0.1:8000/app1/2数据如下:{"name": "xiaoli","age": 25}

六、concreteAPIView(混凝土)

在 五 中我们需要手动继承和组合genenicAPIView和mixins类。genenicAPIView和minxinx提供的功能类进行自由组合。这样继承的类相对来说较多。
因此DRF又提供了写好的genicAPIview和mixIn的组合类。如果不需要自定义的化,可以使用提前定义好的view类

1.组合方式

concreteAPIview组合方式方法功能
CreateAPIViewGenericAPIView, CreateModelMixinpost列表页新增对象功能
ListAPIViewGenericAPIView, ListModelMixinget获得列表页内容
ListCreateAPIViewGenericAPIView, ListModelMixin, CreateModelMixinpost,get完整的列表页功能
RetrieveAPIViewGenericAPIView, RetrieveModelMixinget获取单个对象
UpdateAPIViewGenericAPIView, UpdateModelMixinput、 patch修改单个对象
DestroyAPIViewGenericAPIView, DestroyModelMixindelete删除单个对象
RetrieveUpdateDestroyAPIViewGenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixinget、 put、 patch、 delete完整的详情页查、 改、删 功能

2.路由

总路由和子路由配置不变。和五中的配置保持一致

3.视图使用

视图函数如下:这里需要指定queryset和序列化器类就可以了。
优点:
1.继承的类更加简洁明了
2.不用再写get、post、put、delete等所有方法了。

from rest_framework.generics import ListCreateAPIView,RetrieveUpdateDestroyAPIViewfrom .models import *from .serializers import *class test(ListCreateAPIView):queryset = student.objects.all()serializer_class = student_serializersclass testDetail(RetrieveUpdateDestroyAPIView):queryset = student.objects.all()serializer_class = student_serializers

七、路由Router与视图集ViewSets

concreteAPIView视图类已经非常简练了,已经不用再写操作数据库的逻辑代码。只需要指定数据集合和序列化器类就可以了。
但是目前的状况还是有一点问题:
1.路由要写两条。一条是列表页路由,一条是详情页路由
2.视图函数也是两个,一个是列表页处理函数,一个是详情页处理函数。并且要重复指定queryset和serializer_class

DRF中的router解决了问题1
DRF中的ViewSets解决了问题2

1.配置总路由

编辑urls.py

from django.urls import path,includeurlpatterns = [path('app1/',include('app1.urls')),]

2.配置子路由

编辑app1下的urls.py文件

from django.urls importpathfrom .views import *from rest_framework.routers import SimpleRouterurlpatterns = []router = SimpleRouter()router.register('db',test)# 查看生成了哪些路由条目for url in router.urls:print(f'url = {url}')urlpatterns += router.urls

在启动项目的时候在日志中可以看到生成了两条路由

# 这个对应列表页路由url = <URLPattern '^db/$' [name='student-list']> # 这个对应详情页路由url = <URLPattern '^db/(?P[^/.]+)/$' [name='student-detail']> 

3.配置视图集

这里的视图类也不在用写两个了,写一个就可以了

from rest_framework.viewsets import ModelViewSetfrom .models import *from .serializers import *class test(ModelViewSet):queryset = student.objects.all()serializer_class = student_serializers

测试增删改查查