- Request 相关
- Response 相关
- DRF 状态码
APIView是DRF提供的所有视图的基类,继承自Django的View。与之不同之处在于:
- 传入视图方法对象不同:DRF是Request,DJango是HTTPRequest
- DRF视图方法可以返回Response对象,视图会为响应数据设置(render)符合前端的格式
- APIException异常都会被捕获,并且处理为合适的响应信息
- 在进行dispatch()分发前,会对请求进行身份认证、权限检查、流量控制等
- APIView中仍以常规的类视图定义来实现get、post等请求方法
from rest_framework import serializers
from book.models import Book,Publish
# class Publish(serializers.ModelSerializer):
# class meta:
# model = Publish
# fields = ['publish',]
class BookModelSerializer(serializers.ModelSerializer):
# publish = Publish()
class meta:
model = Book
fields = '__all__'
# views.py
'''
列表视图:
GET /books/ 提供所有记录
POST /books/ 新增一条记录
详情视图:
GET /books// 提供一条记录
PUT /books// 修改一条记录
DELETe /books// 删除一条记录
APIView + ModelSerializer
'''
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from book.models import Book
from book.serializers import BookModelSerializer
class BookListAPIView(APIView):
""" 列表视图 """
def get(self,request):
'''查询所有'''
# 查询所有的书
books = Book.objects.all()
# 序列化数据
serializer = BookModelSerializer(books,many=True)
print(serializer.data)
res = Response(serializer.data)
# 1).data传给response对象的序列化后,但尚未render处理的数据
print(res.data)
# 2).status_code状态码的数字
print(res.status_code)
# 3).content经过render处理后的响应数据
# print(res.content)
# 状态码默认200
return Response(serializer.data)
def post(self,request):
'''新增一条'''
# 获取从前端传过来的请求体数据
data = request.data
# 创建序列化器进行反序列化
serializer = BookModelSerializer(data=data)
# 调用序列化器的is_valid()方法进行校验
serializer.is_valid(raise_exception=True)
# 调用序列化器的save方法进行执行create方法
serializer.save()
# 响应
return Response(data=serializer.data,status=status.HTTP_201_CREATED)
class BookDetailAPIView(APIView):
""" 详情视图 """
def get(self,request,pk):
# 查询为pk模型对象
book = Book.objects.filter(pk=pk).first()
if not book:
return Response(status=status.HTTP_404_NOT_FOUND)
else:
# 使用序列化器类进行序列化
serializer = BookModelSerializer(instance=book)
return Response(serializer.data)
def put(self,request,pk):
# 修改为pk的模型对象
book = Book.objects.filter(pk=pk).first()
if not book:
return Response(status=status.HTTP_404_NOT_FOUND)
else:
data = request.data
serializer = BookModelSerializer(instance=book,data=data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data)
def delete(self,request,pk):
# 修改为pk的模型对象
book = Book.objects.filter(pk=pk).first()
if not book:
return Response(status=status.HTTP_404_NOT_FOUND)
else:
book.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
# urls.py
from django.contrib import admin
from django.urls import path
from book.views import BookListAPIView,BookDetailAPIView
from rest_framework.routers import DefaultRouter
urlpatterns = [
path('admin/', admin.site.urls),
path('books/',BookListAPIView.as_view()),
path('books//',BookDetailAPIView.as_view())
]lo9
# # 指定 DRF-ModelView 路由
# router = DefaultRouter()
# router.register(r'api/books',BookView)
# urlpatterns += router.urls
三、GenericAPIView在增加或者修改时,若有外键,需要将外键的关联部分也放进去才能添加。
3.1 GenericAPIView 单独使用继承自APIView,主要增加了操作序列化器和数据库查询的方法,作用是为下面Mixin扩展类执行提供支持,使用时常常搭配一个或多个Mixin扩展类。除了继承了APIView身份认证、权限检查、流量控制这三个功能外,还新增了分页和过滤。
- 列表视图:self.get_queryset()
- 详情视图:self.get_object(),还多了个pk,或者其它参数
"""
后续面对其他序列化类和数据来源时,只需要替换 step1 和 step2 即可
"""
class BookListGenericView(GenericAPIView):
""" 列表视图 """
# step1 指定序列化器类
serializer_class = BookModelSerializer
# step2 指定查询集,即明确数据来源
queryset = Book.objects.all()
# step3 定义请求方法函数
def get(self,request):
qs = self.get_queryset()
serializer = self.get_serializer(qs,many=True)
return Response(serializer.data)
# 其余同上 post
''' 对于详情视图,只需要在后面指定查询的pk即可
lookup_field = 'pk'
若需要更改查询 pk 可修改 lookup_field 为你所需查询的字段名
'''
class BookDetailGenericView(GenericAPIView):
""" 列表视图 """
# step1 指定序列化器类
serializer_class = BookModelSerializer
# step2 指定查询集,即明确数据来源
queryset = Book.objects.all()
# step3 定义请求方法函数
def get(self,request,pk):
book = self.get_object()
serializer = self.get_serializer(book)
return Response(serializer.data)
# 其余同上 put delete
urlpatterns = [
path('admin/', admin.site.urls),
# path('books/',BookListAPIView.as_view()),
# path('books//',BookDetailAPIView.as_view()),
path('books/',BookListGenericView.as_view()),
path('books//',BookDetailGenericView.as_view())
]
3.2 GenericAPIView & Mixin 实现接口
1)ListModelMixin提供了几种后端视图(对数据资源进行曾删改查)处理流程的实现,如果需要编写的视图属于这五种,则视图可以通过继承相应的扩展类来复用代码,减少自己编写的代码量。这五个扩展类需要搭配GenericAPIView父类,因为五个扩展类的实现需要调用GenericAPIView提供的序列化器与数据库查询的方法。
2)CreateModelMixin列表视图扩展类,提供list(request, *args, **kwargs)方法快速实现列表视图,返回200状态码。该Mixin的list方法会对数据进行过滤和分页。
3)RetrieveModelMixin创建视图扩展类,提供create(request, *args, **kwargs)方法快速实现创建资源的视图,成功返回201状态码。如果序列化器对前端发送的数据验证失败,返回400错误。
4)UpdateModelMixin详情视图扩展类,提供retrieve(request, *args, **kwargs)方法,可以快速实现返回一个存在的数据对象。如果存在,返回200, 否则返回404。
5)DestroyModelMixin更新视图扩展类,提供update(request, *args, **kwargs)方法,可以快速实现更新一个存在的数据对象。同时也提供partial_update(request, *args, **kwargs)方法,可以实现局部更新。成功返回200,序列化器校验数据失败时,返回400错误。
删除视图扩展类,提供destroy(request, *args, **kwargs)方法,可以快速实现删除一个存在的数据对象。成功返回204,不存在返回404。
class BookListMixinGenericView(ListModelMixin, CreateModelMixin, GenericAPIView):
# 指定序列化器类
serializer_class = BookModelSerializer
# 指定数据来源
queryset = Book.objects.all()
def get(self, request):
return self.list(request)
def post(self, request):
return self.create(request)
class BookDetailMixinGenericView(RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin, GenericAPIView):
# 指定序列化器类
serializer_class = BookModelSerializer
# 指定数据来源
queryset = Book.objects.all()
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)
3.3 ListAPIView & CreateAPIView & ListCreateAPIView …
1)CreateAPIView
-
提供 post方法
-
继承自: GenericAPIView、CreateModelMixin
-
提供 get 方法
-
继承自:GenericAPIView、ListModelMixin
-
提供 get 方法
-
继承自: GenericAPIView、RetrieveModelMixin
-
提供 delete 方法
-
继承自:GenericAPIView、DestoryModelMixin
-
提供 put 和 patch 方法
-
继承自:GenericAPIView、UpdateModelMixin
-
提供 get、put、patch方法
-
继承自: GenericAPIView、RetrieveModelMixin、UpdateModelMixin
-
提供get、put、patch、delete方法
-
继承自:GenericAPIView、RetrieveModelMixin、UpdateModelMixin、DestoryModelMixin
class BookListMixinGenericView(ListModelMixin, CreateModelMixin, GenericAPIView):
# 指定序列化器类
serializer_class = BookModelSerializer
# 指定数据来源
queryset = Book.objects.all()
def get(self, request):
return self.list(request)
def post(self, request):
return self.create(request)
class BookListMixinGenericView(ListAPIView,CreateAPIView):
# 指定序列化器类
serializer_class = BookModelSerializer
# 指定数据来源
queryset = Book.objects.all()
class BookListMixinGenericView(ListCreateAPIView):
# 指定序列化器类
serializer_class = BookModelSerializer
# 指定数据来源
queryset = Book.objects.all()
四、视图集
4.1 ViewSet视图集:之前是通过将详情视图和列表视图分开编写,因为存在两个get请求(查询所有和单个),而视图集就是为了统一这些接口,将这两个视图统一写到同一个视图类中,并能够自定义其它方法
使用视图集ViewSet,可以将一系列逻辑相关的动作放到一个类中:
- list() 提供一组数据
- retrieve() 提供单个数据
- create() 创建数据
- update() 保存数据
- destory() 删除数据
ViewSet视图集类不再实现get()、post()等方法,而是实现动作 action 如 list()、create()等。视图集只在使用as_view()方法的时候,才会将action动作与具体请求方式对应上。
class BookViewSet(ViewSet):
""" 视图集:之前是通过将详情视图和列表视图分开编写,
因为存在两个get请求(查询所有和单个),而视图集就是为了统一这些接口,
将这两个视图统一写到同一个视图类中,并能够自定义其它方法 """
def list(self,request):
# 查询所有
books = Book.objects.all()
# 查询集要添加 many=True
serializer = BookModelSerializer(books,many=True)
return Response(serializer.data)
def create(self,request):
serializer = BookModelSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data)
def retrieve(self,request,pk):
# 查询 pk 为 pk 的对象
book = Book.objects.filter(pk=pk).first()
if not book:
return Response(status=status.HTTP_404_NOT_FOUND)
else:
serializer = BookModelSerializer(book)
return Response(serializer.data)
def update(self,request,pk):
# 查询 pk 为 pk 的对象
book = Book.objects.filter(pk=pk).first()
if not book:
return Response(status=status.HTTP_404_NOT_FOUND)
else:
# data 前端传过来的 json 数据
serializer = BookModelSerializer(book, data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data, status=status.HTTP_200_OK)
def destroy(self,request,pk):
book = Book.objects.filter(pk=pk).first()
if not book:
return Response(status=status.HTTP_404_NOT_FOUND)
else:
book.delete()
return Response(status.HTTP_204_NO_CONTENT)
urlpatterns = [
path('admin/', admin.site.urls),
# path('books/',BookListAPIView.as_view()),
# path('books//',BookDetailAPIView.as_view()),
# path('books/',BookListGenericView.as_view()),
# path('books//',BookDetailGenericView.as_view()),
path('books/',BookViewSet.as_view({'get':'list','post':'create'})),
path('books//',BookViewSet.as_view({'get':'retrieve','put':'update','delete':'destory'}))
]
4.2 GenericViewSet
使用ViewSet通常并不方便,因为list、retrieve、create、update、destory等方法都需要自己编写,而这些方法与前面讲过的Mixin扩展类提供的方法同名,所以我们可以通过继承Mixin扩展类来复用这些方法而无需自己编写。但是Mixin扩展类依赖与GenericAPIView,所以还需要继承GenericAPIView。
ModelViewSetGenericViewSet就帮助我们完成了这样的继承工作,继承自GenericAPIView与ViewSetMixin,在实现了调用as_view()时传入字典(如{'get':'list'})的映射处理工作的同时,还提供了GenericAPIView提供的基础方法,可以直接搭配Mixin扩展类使用。
ReadOnlyModelViewSet继承自GenericViewSet,同时包括了ListModelMixin、RetrieveModelMixin、CreateModelMixin、UpdateModelMixin、DestoryModelMixin
继承自GenericViewSet,同时包括了ListModelMixin、RetrieveModelMixin
class BookViewSet(ListModelMixin,RetrieveModelMixin,GenericViewSet):
# class BookViewSet(ModelViewSet):
""" 视图集:之前是通过将详情视图和列表视图分开编写,
因为存在两个get请求(查询所有和单个),而视图集就是为了统一这些接口,
将这两个视图统一写到同一个视图类中,并能够自定义其它方法 """
queryset = Book.objects.all()
serializer_class = BookModelSerializer
class BookViewSet(ReadOnlyModelViewSet):
# class BookViewSet(ModelViewSet):
"""
ReadOnlyModelViewSet: books/ & books/1/ get请求
ModelViewSet: books/ & books/1/ get请求 put delete
"""
queryset = Book.objects.all()
serializer_class = BookModelSerializer
""" 额外定义无pk """
# 在通常的增删改查之外定义的行为,应该为其单独定义路由
# 若果此行为不需要加pk,那么他就是列表视图,
# 但是列表视图只有 list create
# URL:path(r'books/latest/',BookViewSet.as_view({'get':'latest'}))
"""action装饰器参数详解
methods:此操作响应的HTTP方法名称列表。默认仅GET。
detail:必要参数。确定此操作是否应用于实例/详细信息请求或集合/列表请求。detail=False不是详情视图;detail=TRUE 是详情视图
url_path:定义此操作的URL段。默认为被修饰的方法的名称。
url_name:为这个动作定义内部(' reverse ') URL名称。默认为方法名,用下划线代替破折号。
"""
@action(methods="get",detail=False)
def latest(self,request):
pass
""" 额外定义有pk """
# 有pk就是详情视图
# 详情视图有:get put delete
# URL:path(r'books//latest/',BookViewSet.as_view({'get':'read'}))
@action(methods='get',detail=True)
def title(self, request,pk):
book = self.get_object()
book.title = request.data['title']
book.save()
# return Response(self.get_serializer(book).data)
pass
4.3 路由定义
常规定义
""" 额外定义无pk """
# 在通常的增删改查之外定义的行为,应该为其单独定义路由
# 若果此行为不需要加pk,那么他就是列表视图,
# 但是列表视图只有 list create
# URL:path(r'books/latest/',BookViewSet.as_view({'get':'latest'}))
""" 额外定义有pk """
# 有pk就是详情视图
# 详情视图有:get put delete
# URL:path(r'books//latest/',BookViewSet.as_view({'get':'read'}))
DefaultRouter & SimpleRouter 定义
# # 指定 DRF-ModelView 路由
# 此方法只适合在视图集中使用,且只能生成标准的增删改查这五个基础的路由
# 若想要自定义的行为也生成路由,需要在自定义行为使用action行为,并指定相应的请求方法
"""action装饰器参数详解
methods:此操作响应的HTTP方法名称列表。默认仅GET。
detail:必要参数。确定此操作是否应用于实例/详细信息请求或集合/列表请求。detail=False不是详情视图;detail=TRUE 是详情视图
url_path:定义此操作的URL段。默认为被修饰的方法的名称。
url_name:为这个动作定义内部(' reverse ') URL名称。默认为方法名,用下划线代替破折号。
"""
# DefaultRouter,SimpleRouter 唯一区别:DefaultRouter会默认生成一个根路由,SimpleRouter不会
# router = DefaultRouter()
# router.register(r'api/books',BookViewSet)
# urlpatterns += router.urls



