一、Request和Response
REST framework 传入视图的request对象不再是Django默认的HttpRequest对象,而是REST framework提供的扩展了HttpRequest类的**Request**类的对象。
REST framework 提供了**Parser**解析器,在接收到请求后会自动根据Content-Type指明的请求数据类型(如JSON、表单等)将请求数据进行parse解析,解析为类字典对象保存到**Request**对象中。
#DRF中的请求导入方式
from rest_framework.request import Request
Request.data #请求数据
Request.query_params====>Django.GET
常用属性
1).data
`request.data` 返回解析之后的请求体数据。类似于Django中标准的`request.POST`和 `request.FILES`属性,但提供如下特性:
- 包含了解析之后的文件和非文件数据
- 包含了对POST、PUT、PATCH请求方式解析后的数据
- 利用了REST framework的parsers解析器,不仅支持表单类型数据,也支持JSON数据
2).query_params
`request.query_params`与Django标准的`request.GET`相同,只是更换了更正确的名称而已。
2. Response
REST framework提供了一个响应类`Response`,使用该类构造响应对象时,响应的具体数据内容会被转换(render渲染)成符合前端需求的类型。
REST framework提供了`Renderer` 渲染器,用来根据请求头中的`Accept`(接收数据类型声明)来自动转换响应数据到对应格式。如果前端请求中未进行Accept声明,则会采用默认方式处理响应数据,我们可以通过配置来修改默认响应格式。
#DRF中的响应导入方式
from rest_framework.response import Response
Response(data=Response.data,status=200,template_name=None,headers=None,content_type=None)
`data`数据不要是render处理之后的数据,只需传递python的内建类型数据即可,REST framework会使用`renderer`渲染器处理`data`。
`data`不能是复杂结构的数据,如Django的模型类对象,对于这样的数据我们可以使用`Serializer`序列化器序列化处理后(转为了Python字典类型)再传递给`data`参数。
参数说明:
- `data`: 为响应准备的序列化处理后的数据;
- `status`: 状态码,默认200;
- `template_name`: 模板名称,如果使用`HTMLRenderer` 时需指明;
- `headers`: 用于存放响应头信息的字典;
- `content_type`: 响应数据的Content-Type,通常此参数无需传递,REST framework会根据前端所需类型数据来设置该参数。
常用属性:
1).data传给response对象的序列化后,但尚未render处理的数据
2).status_code状态码的数字
3).content经过render处理后的响应数据
2.1 两个基类
1) APIView
from rest_framework.views import APIView
APIView是REST framework提供的所有视图的基类,继承自Django的View父类。
APIView与View的不同之处在于:
(1)传入到视图方法中的是REST framework的`Request`对象,而不是Django的`HttpRequeset`对象;
(2)视图方法可以返回REST framework的`Response`对象,视图会为响应数据设置(render)符合前端要求的格式;
(3)任何`APIException`异常都会被捕获到,并且处理成合适的响应信息;
(4)在进行dispatch()分发前,会对请求进行身份认证、权限检查、流量控制。
支持定义的属性:
authentication_classes 列表或元祖,身份认证类
permissoin_classes 列表或元祖,权限检查类
throttle_classes 列表或元祖,流量控制类
在APIView中仍以常规的类视图定义方法来实现get() 、post() 或者其他请求方式的方法。
使用APIView实现增删改查所有图书:
views.py文件
################1.APIView视图###############################
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
# from django.views import View
# 使用APIView实现增删改查所有图书:
#继承APIView
class BookListAPIView(APIView):
"""列表视图"""
def get(self,request):
#1.查询所有的图书
all=BookInfo.objects.all()
# 2.序列化数据,把模型对象转换成字典
# instance 默认参数
# many=True 显示多个数据
#构造序列对象
ser=BookInfoSerializer(instance=all,many=True)
# print(ser.data)
# response =Response(ser.data)
# print(response.data) #响应对象未渲染处理的数据
# print(response.content) # 处理后要响应给前端的数据
# return response
#响应
return Response(ser.data)
pass
def post(self,request):
'''添加数据'''
#获取前端传入请求参数
data=request.data
#使用序列化器进行反序列哈
ser=BookInfoSerializer(data=data)
#校验 raise_exception显示错误消息
ser.is_valid(raise_exception=True)
#保存
ser.save()
#响应
return Response(ser.data,status=status.HTTP_201_CREATED)
pass
#继承APIView
class BookDataAPIView(APIView):
'''详情视图 '''
def get(self, request,pk):
'''查询指定的模型对象=>必须指定id'''
try:
book=BookInfo.objects.get(id=pk)
except BookInfo.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
# 构造序列化器对象
ser =BookInfoSerializer(instance=book)
# 响应
return Response(ser.data)
pass
def put(self,request,pk):
'''更新指定的模型对象=>必须指定id,需要校验'''
try:
book=BookInfo.objects.get(id=pk)
except BookInfo.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
# 构造序列化器对象,获取前端传入的请求体数据
ser = BookInfoSerializer(instance=book,data=request.data)
#校验
ser.is_valid(raise_exception=True)
#保存
ser.save()
#响应
return Response(ser.data)
pass
def delete(self,request,pk):
'''删除指定的模型对象=>必须指定id'''
'''查询指定的模型对象=>必须指定id'''
try:
book = BookInfo.objects.get(id=pk)
except BookInfo.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
#删除
book.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
urls.py文件
#APIView视图
path('books/',views.BookListAPIView.as_view()), #查询所有的图书数据, 类视图;注意:as_view()
re_path(r'^books/(?P\d+)/$',views.BookDataAPIView.as_view()) #查询详情数据, 类视图;注意:as_view() 必须有()
]
效果:
删除之后,再查看:
2) GenericAPIView
from rest_framework.generics import GenericAPIView
继承自APIVIew,增加了对于列表视图和详情视图可能用到的通用支持方法。通常使用时,可搭配一个或多个Mixin扩展类。
源码如下:
支持定义的属性:
(1) 列表视图与详情视图通用:
queryset 列表视图的查询集
serializer_class 视图使用的序列化器
(2) 列表视图使用:
pagination_class 分页控制类
filter_backends 过滤控制后端
(3) 详情页视图使用:
lookup_field 查询单一数据库对象时使用的条件字段,默认为'`pk`'
lookup_url_kwarg 查询单一数据时URL中的参数关键字名称,默认与look_field相同
常用方法:
使用GenericAPIView实现增删改查所有图书:
views.py文件
################2.GenericAPIView视图###############################
from rest_framework.generics import GenericAPIView
class BookGenericAPIView(GenericAPIView):
'''列表查询'''
# 指定查询集'数据来源'
queryset =BookInfo.objects.all()
# 指定序列化器类
serializer_class =BookInfoSerializer
#查询方法
def get(self,request):
# 获取数据集
find=self.get_queryset()
# 返回序列化器对象
ser=self.get_serializer(find,many=True)
return Response(ser.data)
def post(self, request):
'''添加数据'''
# 获取前端传入请求参数
data = request.data
# 使用序列化器进行反序列哈
ser = BookInfoSerializer(data=data)
# 校验 raise_exception显示错误消息
ser.is_valid(raise_exception=True)
# 保存
ser.save()
# 响应
return Response(ser.data, status=status.HTTP_201_CREATED)
class BookDataGenericAPIView(GenericAPIView):
'''详情视图'''
queryset = BookInfo.objects.all()
serializer_class = BookInfoSerializer
def get(self,request,pk):
'''根据id查询数据'''
book=self.get_object()
ser=self.get_serializer(book)
return Response(ser.data)
pass
def put(self, request, pk):
'''根据id更新数据'''
book = self.get_object()
#请求数据
ser = self.get_serializer(book,request.data)
#校验
ser.is_valid(raise_exception=True)
#保存
ser.save()
return Response(ser.data)
pass
def delete(self, request, pk):
'''根据id删除数据,有可能发生异常'''
try:
book = self.get_object()
except BookInfo.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
# 删除模型对象
book.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
pass
urls.py文件
# GenericAPIView视图
path('booksg/', views.BookGenericAPIView.as_view()), # 查询所有的图书数据, 类视图;注意:as_view()
re_path(r'^booksg/(?P\d+)/$', views.BookDataGenericAPIView.as_view()), # 查询详情数据, 类视图;注意:as_view() 必
]
2.2五个扩展类
源码:
views.py
from rest_framework.mixins import ListModelMixin
from rest_framework.generics import GenericAPIView #指定过滤化器queryset
class BookListModelMixin(ListModelMixin,GenericAPIView):
'''列表视图'''
#指定查询集 -数据来源
queryset=BookInfo.objects.all()
#指定序列化器
serializer_class = BookInfoSerializer
def get(self,request):
return self.list(request)
urls.py
# ListModelMixin
path('books_lmm/',views.BookListModelMixin.as_view())
2.CreateModelMixin:创建资源的视图
源码:
views.py
from rest_framework.mixins import CreateModelMixin
class BookCreateModelMixinView(CreateModelMixin,ListModelMixin,GenericAPIView):
'''创建视图'''
#指定查询集 -数据来源
queryset=BookInfo.objects.all()
#指定序列化器
serializer_class = BookInfoSerializer
#查询
def get(self,request):
return self.list(request)
#添加
def post(self,request):
return self.create(request)
urls.py
#CreateModelMixin
path('books_cmm/',views.BookCreateModelMixinView.as_view()),
发生异常;
RuntimeError: You called this URL via POST, but the URL doesn't end in a slash and you have APPEND_SLASH set. Django can't redirect to the slash URL while maintaining POST data. Change your form to point to 127.0.0.1:8000/books_cmm/ (note the trailing slash), or set APPEND_SLASH=False in your Django settings.
解决:
访问时不应该访问ip/books,
而应该访问ip/books/ 少了一个斜杠
3. RetrieveModelMixin 详情视图
源码:
views.py
from rest_framework.mixins import RetrieveModelMixin
class BookRetrieveModelMixinView(RetrieveModelMixin,GenericAPIView):
'''详情视图'''
#指定查询集 -数据来源
queryset=BookInfo.objects.all()
#指定序列化器
serializer_class = BookInfoSerializer
#查询
def get(self,request,pk):
return self.retrieve(request)
urls.py
# RetrieveModelMixin
re_path(r'^books_xmm/(?P\d+)/$', views.BookRetrieveModelMixinView.as_view()), # 查询详情数据, 类视图;注意:as_view() 必
4 UpdateModelMixin: 更新视图
源码:
views.py
from rest_framework.mixins import UpdateModelMixin
class BookUpdateModelMixinView(UpdateModelMixin,RetrieveModelMixin, GenericAPIView):
'''更新视图'''
# 指定查询集 -数据来源
queryset = BookInfo.objects.all()
# 指定序列化器
serializer_class = BookInfoSerializer
# 查询一个
def get(self, request,pk):
return self.retrieve(request,pk)
# 更新
def put(self, request,pk):
return self.update(request,pk)
urls.py
#UpdateModelMixin
re_path(r'^books_umm/(?P\d+)/$',views.BookUpdateModelMixinView.as_view()),
5 DestroyModelMixin:删除视图
源码:
views.py
from rest_framework.mixins import DestroyModelMixin
class BookDestroyModelMixinView(DestroyModelMixin,RetrieveModelMixin,UpdateModelMixin, GenericAPIView):
'''删除视图'''
# 指定查询集 -数据来源
queryset = BookInfo.objects.all()
# 指定序列化器
serializer_class = BookInfoSerializer
# 查询一个
def get(self, request,pk):
return self.retrieve(request,pk)
# 更新
def put(self, request,pk):
return self.update(request,pk)
# 删除
def delete(self, request,pk):
return self.destroy(request,pk)
Urls.py
# DestroyModelMixin
re_path(r'^books_umm/(?P\d+)/$',views.BookDestroyModelMixinView.as_view()),
2.3 子类视图
2.4视图集ViewSet
使用视图集ViewSet,可以将一系列逻辑相关的动作放到一个类中:
- GET: list() 提供一组数据
- GET: retrieve() 提供单个数据
- POST :create() 创建数据
- PUT :update() 更新数据
- PATCH: partail_update, 更新部分数据
- DELETE:destory() 删除数据
ViewSet视图集类不再实现get()、post()等方法,而是实现动作 action 如 list() 、create() 等。继承自APIView,作用也与APIView基本类似,提供了身份认证、权限校验、流量管理等。在ViewSet中,没有提供任何动作action方法,需要我们自己实现action方法。
视图集只在使用as_view()方法的时候,才会将action动作与具体请求方式对应上
from rest_framework.viewsets import ViewSet
class BookViewSet(ViewSet):
'''视图集'''
def list(self,request):
'''展示列表数据'''
#获取数据
all=BookInfo.objects.all()
#序列化
ser=BookInfoSerializer(all,many=True)
#返回数据
return Response(ser.data)
def retrieve(self,request,pk):
'''获取单个数据'''
try:
book=BookInfo.objects.get(id=pk)
except BookInfo.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
ser=BookInfoSerializer(book)
return Response(ser.data)
#视图集 ViewSet
#as_view中传入字典参数: 第一个参数:get,post,put,delete等等; 第二个参数:关联的方法名
#as_view中不能定义相同的请求
path('booksv/', views.BookViewSet.as_view({"get": "list"})),
re_path(r'^booksv/(?P\d+)/$',views.BookViewSet.as_view({"get":"retrieve"})),
2.4.1 ModelViewSet
继承自GenericAPIVIew,同时包括了ListModelMixin、RetrieveModelMixin、CreateModelMixin、UpdateModelMixin、DestoryModelMixin。
##########################视图集################
from rest_framework.viewsets import ViewSet
class BookViewSet(ViewSet):
'''视图集'''
def list(self,request):
'''展示列表数据'''
#获取数据
all=BookInfo.objects.all()
#序列化
ser=BookInfoSerializer(all,many=True)
#返回数据
return Response(ser.data)
def retrieve(self,request,pk):
'''获取单个数据'''
try:
book=BookInfo.objects.get(id=pk)
except BookInfo.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
ser=BookInfoSerializer(book)
return Response(ser.data)
#视图集 ViewSet
#as_view中传入字典参数: 第一个参数:get,post,put,delete等等; 第二个参数:关联的方法名
#as_view中不能定义相同的请求
path('booksv/', views.BookViewSet.as_view({"get": "list"})),
re_path(r'^booksv/(?P\d+)/$',views.BookViewSet.as_view({"get":"retrieve"})),
2.4.2视图集中定义附加action动作
在视图集中,除了上述默认的方法动作外,还可以添加自定义动作。
添加自定义动作需要使用`rest_framework.decorators.action`装饰器。
以action装饰器装饰的方法名会作为action动作名,与list、retrieve等同。
action装饰器可以接收两个参数:
True 表示使用通过URL获取的主键对应的数据对象
False 表示不使用URL获取主键
##########################ModelViewSet################
from rest_framework.decorators import action
from rest_framework.viewsets import ModelViewSet
class BookViewSetAction(ModelViewSet):
queryset = BookInfo.objects.all()
serializer_class = BookInfoSerializer
@action(methods=['get'],detail=False)
def later(self,request):
'''最新的图书信息'''
# 获取最后一本书
book =BookInfo.objects.latest('id')
serializer=self.get_serializer(book)
return Response(serializer.data)
# detail为True,表示要处理具体与pk主键对应的BookInfo对象
# books/pk/read/
@action(methods=['put'],detail=True)
def readCount(self,request,pk):
"""修改图书的阅读量数据"""
book =self.get_object()
book.read_count=request.data.get('read_count')
book.save()
serializer=self.get_serializer(book)
return Response(serializer.data)
#ModelViewSet 注意:遵循ViewSet的规则,必须传字典参数哦!!否则发生错误!!
# ypeError: The `actions` argument must be provided when calling `.as_view()` on a ViewSet. For example `.as_view({'get': 'list'})`
#视图集中包含附加action的
path('books_m/', views.BookViewSetAction.as_view({'get': 'later'})),
re_path(r'^booksm/(?P\d+)/read/$', views.BookViewSetAction.as_view({'put':'readCount'})),
http://127.0.0.1:8000/booksm/13/read/
2.5. DefaultRouter与SimpleRouter的区别
DefaultRouter与SimpleRouter的区别是,DefaultRouter会多附带一个默认的API根视图,返回一个包含所有列表视图的超链接响应数据。`DefaultRouter继承自SimpleRouter`
而使用SimpleRouter则会报错