【一】两个视图基类(APIView和GenericAPIView)
【1】基于APIView+序列化类+Response写5个接口
-
路由
from django.urls import path from .views import BookView, BookDetailView urlpatterns = [ path('books/', BookView.as_view()), path('books/<int:pk>/', BookDetailView.as_view()), ]
-
视图类
from rest_framework.response import Response from rest_framework.views import APIView from .models import Book from .serializer import BookSerializer from rest_framework.parsers import JSONParser, FormParser, MultiPartParser from rest_framework.renderers import JSONRenderer, BrowsableAPIRenderer class BookView(APIView): # 在视图类上,配置---》只运行json格式 parser_classes = [JSONParser] renderer_classes = [JSONRenderer] def post(self, request): serializer = BookSerializer(data=request.data) serializer.is_valid(raise_exception=True) serializer.save() return Response({"code": 200, "msg": "添加成功", "result": serializer.data}) def get(self, request): books = Book.objects.all() serializer = BookSerializer(instance=books, many=True) return Response({"code": 200, "msg": "查看成功", "result": serializer.data}) class BookDetailView(APIView): # 在视图类上,配置---》只运行json格式 parser_classes = [JSONParser] renderer_classes = [JSONRenderer] def put(self, request, pk): book = Book.objects.filter(pk=pk).first() serializer = BookSerializer(instance=book, data=request.data) serializer.is_valid(raise_exception=True) serializer.save() return Response({"code": 200, "msg": "修改成功", "result": serializer.data}) def get(self, request, pk): book = Book.objects.filter(pk=pk).first() serializer = BookSerializer(instance=book) return Response({"code": 200, "msg": "查看成功", "result": serializer.data}) def delete(self, request, pk): Book.objects.filter(pk=pk).delete() return Response({"code": 200, "msg": "删除成功"})
-
数据模型表
from django.db import models # Create your models here. from django.db import models # Create your models here. class Student(models.Model): name = models.CharField(max_length=32) age = models.IntegerField() gender = models.IntegerField(choices=((0, '未知'), (1, '男'), (2, '女'))) @property def new_name(self): return self.name + "_nb" # book数据表 class Book(models.Model): name = models.CharField(max_length=32) price = models.IntegerField() publish = models.ForeignKey(to='Publish', on_delete=models.SET_NULL, null=True) # 作者和图书是多对多关系,需要中间表 # Django中orm优化了,无序手动创建 使用ManyToMany可以自动创建 # authors不是字段,是一个中间表 authors = models.ManyToManyField(to='Author') def publish_detail(self): return {"name": self.publish.name, "city": self.publish.city, "addr": self.publish.addr} def author_list(self): author_all = self.authors.all() author_list = [] for author in author_all: author_list.append({"name": author.name, "age": author.age}) return author_list class Publish(models.Model): name = models.CharField(max_length=32) city = models.CharField(max_length=32) addr = models.CharField(max_length=32) class Author(models.Model): name = models.CharField(max_length=32) age = models.IntegerField() addr = models.CharField(max_length=32)
-
序列化类
from rest_framework import serializers from app01.models import Book class BookSerializer(serializers.ModelSerializer): class Meta: model = Book fields = ['name', 'price', 'publish', 'authors', 'publish_detail', 'author_list'] extra_kwargs = { "publish": {"write_only": True}, "authors": {"write_only": True}, "publish_detail": {"read_only": True}, "author_list": {"read_only": True}, }
【APIView源码分析】
# APIView 1 它继承自django的View,所以必须写get、post、put、delete方法 2 在它里面还有类属性:parser_class,render_class -使用顺序是:视图类自己--》项目配置文件--》drf内置配置 -render_class=api_settings.DEFAULT_RENDERER_CLASSES -api_settings是drf的配置,如果咱们项目配置文件配了 REST_FRAMEWORK={},会覆盖掉原来drf默认的配置 3 读APIViwez--》as_view和dispatch 在APIView里面做了下面这些 -3.1 包装了新的request: -新的request对象:request._request -视图类的对象 :self.requset 就是新的request -3.2 去除了csrf认证 -csrf_exempt(view) -3.3 做了三大认证:认证,权限,频率 self.perform_authentication(request) self.check_permissions(request) self.check_throttles(request) -3.4 做了全局异常处理:后续要学定制返回格式 在try里 4 总结: 当使用的时候 def get(self,request): # request 新的 # csrf没了 # 执行完三大认证,才能到这里 # 出了异常,会有捕获 request._request self.request=request
【2】基于GenericAPIView + 序列化类 + Response写5个接口
-
views.py
# 基于genericAPIView + Response from rest_framework.generics import GenericAPIView class BookView(GenericAPIView): # 两个类属性 # 1 要序列化的数据、 queryset = Book.objects.all() # 2 序列化类 serializer_class = BookSerializer def post(self, request): # self.get_serializer 拿到在类属性上配置的serializer_class的值 serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) serializer.save() return Response({"code": 200, "msg": "添加成功", "result": serializer.data}) def get(self, request): books = self.get_queryset() serializer = self.get_serializer(instance=books, many=True) return Response({"code": 200, "msg": "查看成功", "result": serializer.data}) class BookDetailView(GenericAPIView): queryset = Book.objects.all() serializer_class = BookSerializer def put(self, request, pk): # 根据传入的pk值,拿到一条记录 # self.get_object()---》 根据queryset 配置所有数据和pk 获取一条数据 book = self.get_object() serializer = self.get_serializer(instance=book, data=request.data) serializer.is_valid(raise_exception=True) serializer.save() return Response({"code": 200, "msg": "修改成功", "result": serializer.data}) def get(self, request, pk): book = self.get_object() serializer = self.get_serializer(instance=book) return Response({"code": 200, "msg": "查看成功", "result": serializer.data}) def delete(self, request, pk): self.get_object().delete() return Response({"code": 200, "msg": "删除成功"})
继承GenericAPIView后使用了两个类属性和三个方法
类属性:
# 所有数据 queryset = Book.objects.all() # 序列化类 serializer_class = BookSerializer
方法:使用对象调用 self.方法
# 和之前的序列化类一样 serializer = self.get_serializer(instance=book, data=request.data) # 所有数据 books = self.get_queryset() # 根据pk值获取单条数据 book = self.get_object()
【总结】假设用户再写其他类的5个接口 代码只需要更改两个类属性
# 所有数据 queryset = Book.objects.all() # 序列化类 serializer_class = BookSerializer
【GenericAPIView源码分析】
1 继承了APIView ---》APIView具备的所有特点,GenericAPIView都有,在此基础上扩展了一些用法 2 多了几个类属性 queryset # 数据源,直接给列表 或 QuerySet serializer_class # 序列化类 lookup_field = 'pk' # get_object,没有传参数,但它是按pk拿数据 lookup_url_kwarg = None # lookup_url_kwarf=1 filter_backends = api_settings.DEFAULT_FILTER_BACKENDS # 过滤 pagination_class = api_settings.DEFAULT_PAGINATION_CLASS # 分页 3 多了几个方法 (1)get_queryset:本质就是返回了self.queryset 数据源 def get_queryset(self): queryset = self.queryset if isinstance(queryset, QuerySet): queryset = queryset.all() # 因为self.queryset 会先执行--》为了保证数据最新 return queryset (2)get_serializer:直接实例化得到序列化类的对象--》self.serializer_class def get_serializer(self, *args, **kwargs): serializer_class = self.get_serializer_class() kwargs.setdefault('context', self.get_serializer_context()) return serializer_class(*args, **kwargs) def get_serializer_class(self): # 通过重写它,定制序列化,反序列化分开使用 return self.serializer_class # 总结: -内部方法中拿序列化类的对象:get,post--》都是通过self.get_serializer() -但是我们可以通过【重写】get_serializer_class,返回不同序列化类来控制:self.get_serializer()拿到的序列化类不同 # 示例 # 要求,序列化用 BookSerialzier 序列化类,反序列化用 BookWriteSerialzier class BookView(ListCreateAPIView): queryset = Book.object.all() serializer_class = BookSerialzier def get_serializer_class(self): if self.request.method=='GET': return BookSerialzier else: return BookWriteSerialzier (3)get_object:根据 lookup_field 指定的对应参数,从数据源中 过滤出单个对象
【补充】
#1 对象使用类属性 对象.属性名即可 类.属性名即可 #2 如果对象中有跟类属性同名的,会怎么样? 优先用自己的 class Person: school='清华大学' person2=Person() person2.school='北京大学' # 只会改对象自己的,不会改类的 print(person2.school) # 一旦对象自己有了,优先用对象自己的 '北京大学' person=Person() print(person.school) #对象没有,就用类的 清华大学
【3】5个视图扩展类 + GenericAPIView + 序列化类 + Response写接口
# 5个视图扩展类 + GenericAPIView + 序列化类 + Response写接口 from rest_framework.generics import GenericAPIView # 5个视图扩展类 from rest_framework.mixins import RetrieveModelMixin, UpdateModelMixin, CreateModelMixin, DestroyModelMixin, \ ListModelMixin class BookView(GenericAPIView, ListModelMixin, CreateModelMixin): # 两个类属性 # 1 要序列化的数据、 queryset = Book.objects.all() # 2 序列化类 serializer_class = BookSerializer def post(self, request): return super().create(request) def get(self, request): return super().list(request) class BookDetailView(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin): queryset = Book.objects.all() serializer_class = BookSerializer def put(self, request, pk): return super(BookDetailView, self).update(request, pk) def get(self, request, pk): return super(BookDetailView, self).retrieve(request, pk) def delete(self, request, pk): return super(BookDetailView, self).destroy(request, pk)
【注意】
-
5个视图扩展类必须配合GenericAPIView使用
【5个视图扩展类的源码分析】
from rest_framework.mixins import RetrieveModelMixin, UpdateModelMixin, CreateModelMixin, DestroyModelMixin,ListModelMixin # CreateModelMixin class CreateModelMixin: def create(self, request, *args, **kwargs): # 拿到序列化类,实例化得到对象 serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) # 保存单独封装 # 可以通过重写perform_create 在保存之前或者保存之后做的处理 self.perform_create(serializer) return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) def perform_create(self, serializer): # 保存之前要做的处理 serializer.save() # 保存之后要做的处理 ListModelMixin::list -获取所有 DestroyModelMixin: destroy -删除 -perform_destroy RetrieveModelMixin: retrieve -拿单条数据 UpdateModelMixin:update -更新 -perform_update
【4】9个视图子类
# 9个视图子类---这是真是的视图类 from rest_framework.generics import ListAPIView, CreateAPIView, RetrieveAPIView, UpdateAPIView, DestroyAPIView from rest_framework.generics import ListCreateAPIView from rest_framework.generics import RetrieveUpdateDestroyAPIView, RetrieveUpdateAPIView, RetrieveDestroyAPIView class BookView(ListCreateAPIView): # 两个类属性 # 1 要序列化的数据、 queryset = Book.objects.all() # 2 序列化类 serializer_class = BookSerializer # 定制新增之后返回格式 def create(self, request, *args, **kwargs): res = super().create(request) return Response({"code": 200, "msg": "新增成功", "result": res.data}) class BookDetailView(RetrieveUpdateDestroyAPIView): queryset = Book.objects.all() serializer_class = BookSerializer
【9个视图子类源码分析】
1 ListAPIView -GenericAPIView+ListModelMixin+get方法 -只写一个查询所有接口 class BookView(ListAPIView): queryset=xx serializer_class=yy 2 CreateAPIView -GenericAPIView+CreateModelMixin+post方法 -只写一个新增接口 class BookView(CreateAPIView): queryset=xx serializer_class=yy def perform_save(self): pass 3 RetrieveAPIView -GenericAPIView+RetrieveModelMixin+get方法 -只写一个查询所有接口 class BookView(ListAPIView): queryset=xx serializer_class=yy 4 UpdateAPIView 修改 5 DestroyAPIView 删除 6 ListCreateAPIView -GenericAPIView+ListModelMixin+CreateModelMixin+post方法+get方法 -写一个查询所有接口和新增接口 class BookView(ListCreateAPIView): queryset=xx serializer_class=yy 7 RetieveUpdateDestroyAPIView -GenericAPIView+RetrieveModelMixin+UpdateModelMixin++DestroyModelMixin+put方法+get方法+delete方法 -写一个查询单条,修改一条,删除一条 class BookView(RetrieveUpdateDestroyAPIView): queryset=xx serializer_class=yy 8 RetieveUpdateAPIView 查询、修改 9 RetieveDestroyAPIView 查询、删除
【5】视图集
-
使用视图集ModelViewSet, ReadOnlyModelViewSet时路由就需要改变一些了,添加映射
from django.urls import path from .views import BookView # 继承ModelViewSet后路由就变了,需要映射 urlpatterns = [ path('books/', BookView.as_view({"get": "list", "post": "create"})), path('books/<int:pk>/', BookView.as_view({"get": "retrieve", "put": "update", "delete": "destroy"})), ]
-
views.py
# 视图集 from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet class BookView(ModelViewSet): queryset = Book.objects.all() serializer_class = BookSerializer # 这个类中没有 get post get put delete方法 # 相对应的是 list retrieve create update destroy方法 c # 需要定制输出格式的时候就需要重写list、retrieve、create、update、destroy方法 # 只读里面只有两个方法 mixins.RetrieveModelMixin,mixins.ListModelMixin class BookRView(ReadOnlyModelViewSet): queryset = Book.objects.all() serializer_class = BookSerializer
【视图集源码分析】
# ModelViewSet,ReadOnlyModelViewSet # ModelViewSet:5个接口都有 里面没有get,post,delete方法了,因为路由写法变了,是映射的 CreateModelMixin ListModelMixin DestroyModelMixin RetrieveModelMixin UpdateModelMixin GenericViewSet -GenericAPIView -ViewSetMixin #ReadOnlyModelViewSet:查询单条和所有接口 ListModelMixin RetrieveModelMixin GenericViewSet -GenericAPIView -ViewSetMixin # ViewSetMixin:控制路由写法变了的类。以后只要路由写法变了,必须继承它 -重写了urls.py中的as_view,必须传字典做映射的方式,否则报错 -原理是通过反射:实现了MyViewSet.as_view({'get': 'list', 'post': 'create'}) -要执行get的时候,本质执行的是 list -好处,以后视图类,可以写任意名字的方法,只要路由做好映射即可 -路由:path('send_sms/', UserView.as_view({'get':'send_sms'})), -视图类: class UserView(ViewSetMixin,APIView): def send_sms(self): print('真发短信') return Response({'code':100,'msg':"短信发送成功"}) # ViewSet = ViewSetMixin,APIView # GenericViewSet=ViewSetMixin+GenericAPIView from rest_framework.response import Response class UserDetailView(GenericViewSet): serializer_class = 某个序列化类 def login(self): serializer=self.get_serializer() serializer.is_valiad() return Response({'code':100,'msg':"短信发送成功"})
【6】视图层总结-小案例选择哪些视图类继承
# 1 发送短信接口 -APIView+get方法 -ViewSet+send_sms方法 # 3 查询所有,查询单条 -路由: path('books/', BookView.as_view({'get':'list'})), path('books/<int:pk>', BookView.as_view({'get':'retrieve'})), -视图类 class BookView(ReadOnlyModelViewSet): queryset= serializer_class= # 4 查询所有,删除一条 -写俩:ListAPIView DestroyAPIView -写一个: -路由: path('books/', BookView.as_view({'get':'list'})), path('books/<int:pk>', BookView.as_view({'delete':'destroy'})), -视图类 class BookView(GenericViewSet,ListModelMixin,DestroyModelMixin): queryset= serializer_class= # 5 总结 1 只要想路由变了:ViewsetMixin 2 只要想序列化,反序列化,就用GenericAPIView 3 即想路由变了又想序列化,反序列化 GenericViewSet 4 即想路由变了又想序列化,反序列化 ,还想写某一个或某几个接口 GenericViewSet+5个视图扩展类