一、基本概念
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 | 组合方式 | 方法 | 功能 |
---|---|---|---|
CreateAPIView | GenericAPIView, CreateModelMixin | post | 列表页新增对象功能 |
ListAPIView | GenericAPIView, ListModelMixin | get | 获得列表页内容 |
ListCreateAPIView | GenericAPIView, ListModelMixin, CreateModelMixin | post,get | 完整的列表页功能 |
RetrieveAPIView | GenericAPIView, RetrieveModelMixin | get | 获取单个对象 |
UpdateAPIView | GenericAPIView, UpdateModelMixin | put、 patch | 修改单个对象 |
DestroyAPIView | GenericAPIView, DestroyModelMixin | delete | 删除单个对象 |
RetrieveUpdateDestroyAPIView | GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin | get、 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
测试增删改查查