DRF封装了很多关于请求信息的参数,我们可以通过 request获取,响应 Response 也有着规定的模板,让接口响应更加规范。

在视图中,封装了非常多的视图类,便于简化视图层操作。

  • APIView是DRF中提供的所有视图类的积累,它继承于django.views.View,传入的请求是DRF的 Request实例,任何APIException异常都会被捕获,并且传递给合适的响应 ,业务代码需要自己写

  • ViewSet,继承自APIView,没有继承拓展类,业务逻辑还得自己去写,提供action参数,进行视图和路由 的绑定,不再使用http请求作为视图方法 get post put delete 可以自己指定路由和方法绑定,实现把关于一个模型的操作都写在一个类里

  • GenericViewSet,继承自GenericAPIView,没有继承拓展类,业务逻辑还得自己去写,把部分公共代码实现了服用。不再使用http请求作为视图方法 get post put delete 可以自己指定路由和方法绑定,实现把关于一个模型的操作都写在一个类里

两个拓展视图集:

  • ModelViewSet(增删改查都可以实现了),继承了GenericAPIView,继承了五个拓展类

  • ReadOnlyModelViewSet(实现了获取多个数据对象和获取单一数据对象),继承了GenericAPIViewListModelMixinRetrieveModelMixin

路由…

请求响应

Request

新建一个req应用,用于专门编写本次实验的例子,视图层代码如下;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def get(self,request):
'''
request:<rest_framework.request.Request: GET '/req/s1'> 用的drf
request._request:<WSGIRequest: GET '/req/s1'>
:param request:
:return:
'''
print(f'request:{request}')
print(f'request._request:{request._request}')
print('drf提供的request常用操作-------------------------------------')
print(f'用户:{request.user}')
print(f'地址栏参数:{request.query_params}')
print(f'地址栏参数转字典:{request.query_params.dict()}')
return Response({'msg':'ok'})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def post(self,request):
'''
请求体:<QueryDict: {'name': ['小王'], 'files': [<InMemoryUploadedFile: 1_Request.txt (text/plain)>, <InMemoryUploadedFile: 1_Response.txt (text/plain)>]}>
请求体转字典:{'name': '小王', 'files': <InMemoryUploadedFile: 1_Response.txt (text/plain)>}
{<InMemoryUploadedFile: 1_Response.txt (text/plain)>}
请求体中的文件列表:<MultiValueDict: {'files': [<InMemoryUploadedFile: 1_Request.txt (text/plain)>, <InMemoryUploadedFile: 1_Response.txt (text/plain)>]}>
{'ALLUSERSPROFILE': 'C:\\ProgramData', 'APPDATA': 'C:\\Users\\Luffy\\AppData\\Roaming', 省略}
NSFOCUS
'''
print(f'请求体:{request.data}')
print(f'请求体转字典:{request.data.dict()}')
print({request.data.dict().get('files')})
print(f'请求体中的文件列表:{request.FILES}')
# 获取请求头
print(request._request.META)
# 自定义请求头 要用HTTP_大写单词获取
print(request._request.META.get('HTTP_COMPANY'))
return Response({'msg': 'ok'})

请求中很多键值对的数据类型并不是用的Python的字典,所以需要获取值的时候要转为字典再获取

Response

Response(data, status=None, template_name=None, headers=None, content_type=None)

返回的模板如上,举个例子

1
return Response(data=serializer.data,status=status.HTTP_201_CREATED,headers={'company':'NSFOCUS'},content_type='application/json')

这个每个固定的,知道里面需要传的参数即可

视图

在开篇也已经说到了各个视图类的基本内容,直接上代码:

APIView

url.py

1
2
path('stu3/<int:pk>/',TeacherInfoAPIView.as_view()), # 单个
path('stu3/',TeacherListAPIView.as_view()), # 多个

views.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
class TeacherListAPIView(APIView):
'''APIView不支持自定义方法,只支持get post put delete的https请求'''
def get(self,request):
'''
获取list
:param request:
:return:
'''
teacher = Teacher.objects.all()
serializer = TeacherModelSerializer(instance=teacher, many=True)
return Response(data=serializer.data, status=status.HTTP_200_OK)

def post(self,request):
'''
新增数据
:param request:
:return:
'''
serializer = TeacherModelSerializer(data=request.data.dict())
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(data=serializer.data, status=status.HTTP_201_CREATED)

class TeacherInfoAPIView(APIView):
def get(self,request,pk):
'''根据id查询老师信息'''
teacher = Teacher.objects.get(pk=pk)
serializer = TeacherModelSerializer(instance=teacher)
return Response(data=serializer.data, status=status.HTTP_200_OK)

def put(self,request,pk):
'''根据id更新'''
data = request.data.dict()
teacher = Teacher.objects.get(pk=pk)
serializer = TeacherModelSerializer(instance=teacher,data=data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(data=serializer.data, status=status.HTTP_200_OK)

def delete(self,request,pk):
'''根据id删除,这里直接删了,因为数据库中没有isDeleted字段'''
data = request.data.dict()
teacher = Teacher.objects.get(pk=pk)
teacher.delete()
return Response(status=status.HTTP_200_OK)

ViewSet

url.py

1
2
3
4
5
6
path('stu4',TeacherViewSet.as_view(actions={'get':'list',
'post':'add',
})),

path('stu4/<int:pk>/', TeacherViewSet.as_view(actions={'get': 'get_teacherById',
})),

view.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class TeacherViewSet(ViewSet):
'''
没有继承拓展类,业务逻辑还得自己去写,提供action参数,进行视图和路由 的绑定,
不再使用http请求作为视图方法 get post put delete 可以自己指定路由和方法绑定,
实现把关于一个模型的操作都写在一个类里
'''
def list(self,request):
teacher = Teacher.objects.all()
serializer = TeacherModelSerializer(instance=teacher, many=True)
return Response(data=serializer.data, status=status.HTTP_200_OK)

def add(self,request):
serializer = TeacherModelSerializer(data=request.data.dict())
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(data=serializer.data, status=status.HTTP_201_CREATED)

def get_teacherById(self,request,pk):
'''根据id查询老师信息'''
teacher = Teacher.objects.get(pk=pk)
serializer = TeacherModelSerializer(instance=teacher)
return Response(data=serializer.data, status=status.HTTP_200_OK)

写了几个,就是可以自定义函数方法了

GenericViewSet

可以把我们经常写的模型和序列化器代码抽取出来

url.py

1
2
3
4
5
6
path('stu5', TeacherGenericViewSet.as_view(actions={'get': 'list',
'post': 'create',
})),

path('stu5/<int:pk>/', TeacherGenericViewSet.as_view(actions={'get': 'get_one',
})),

view.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class TeacherGenericViewSet(GenericViewSet):
queryset = Teacher.objects.all()
serializer_class = TeacherModelSerializer

def list(self, request):

serializer = self.get_serializer(instance=self.get_queryset(), many=True)
return Response(serializer.data)

def create(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)

def get_one(self, request, pk):
try:
instance = self.get_object()
# 实例化序列化器对象
serializer = self.get_serializer(instance=instance)
return Response(data=serializer.data, status=status.HTTP_200_OK)
except Student.DoesNotExist:
return Response({'msg': '老师不存在'}, status=status.HTTP_404_NOT_FOUND)

ModelViewSet

实现了五个拓展类,所以写两行代码即可有5个http接口

url.py

1
2
3
4
5
6
7
from rest_framework.routers import DefaultRouter,SimpleRouter
router = DefaultRouter()
# 注册试图(访问前缀,视图集,调用别名)
router.register(prefix='s9',viewset=StudentMixinViewSet,basename='s9')
# 路由列表

urlpatterns += router.urls

view.py

1
2
3
class StudentModelViewSet(ModelViewSet):
queryset = Student.objects.all()
serializer_class = StudentModelSerializer

ReadOnlyModelViewSet

只能读

url.py

1
2
3
4
5
6
7
from rest_framework.routers import DefaultRouter,SimpleRouter
router = DefaultRouter()
# 注册试图(访问前缀,视图集,调用别名)
router.register(prefix='stu6',viewset=StudentModelViewSet,basename='stu6')
router.register(prefix='stu7',viewset=StudentReadOnlyModelViewSet,basename='stu7')
# 路由列表
urlpatterns += router.urls

view.py

1
2
3
4
5
6
7
8
9
10
'''
ViewSet
GenericViewSet
ModelViewSet = GenericViewSet + ListModelMixin + CreateModelMixin + UpdateModelMixin+
RetrieveModelMixin + DestoryModelMixin
ReadOnlyModelViewSet = GenericViewSet + ListModelMixin + RetrieveModelMixin
'''
class StudentReadOnlyModelViewSet(ReadOnlyModelViewSet):
queryset = Student.objects.all()
serializer_class = StudentModelSerializer

路由

REST框架添加了对自动URL路由到Django的支持,并为你提供了一种简单、快速和一致的方式来将视图逻辑连接到一组URL。

在上面编写视图用例的已经写到了两种路由,第一种和Django类似,但是通过 as_view(),自适应路由(我这样理解的,不清楚对不对),就是可以根据试图层的http接口自己生成路由,在 APIView中不需要写参数。但在GenericViewSet中或者继承了GenericViewSet的视图模板中,不仅仅有http借口,此时就可以添加参数action来进行视图和路由的匹配。

对于ModelViewSet和ReadOnlyModelViewSet可以使用DefaultRouter和SimpleRouter,DefaultRouter和SimpleRouter两者简单的区别就是前者提供API页面,SimpleRouter后者只是简单的数据返回。

关于DRF路由的写法,还有自定义路由等等,可以参考中文官方文档

Django REST framework 中文文档