Django REST framework
Django REST framework官方文档:点击 中文文档:点击
- 安装djangorestframework:pip3 install djangorestframework (pip3 list 查看详情和版本信息)
- 注册rest_framework(settings.py)
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'app01', 'rest_framework', ]
DRF序列化
做前后端分离的项目,前后端交互一般都选择JSON数据格式,JSON是一个轻量级的数据交互格式。后端给前端数据的时候都要转成json格式,那就需要对从数据库拿到的数据进行序列化。
要用DRF的序列化,就要遵循人家框架的一些标准
- 声明序列化类(app01目录下新建serializers.py文件)
- Django CBV继承类是View,现在DRF要用APIView
- Django中返回的时候用HTTPResponse,JsonResponse,render ,DRF用Response
-
DRF反序列化
- 当前端给后端发post的请求的时候,前端给后端传过来的数据,要进行一些校验然后保存到数据库。
- 这些校验以及保存工作,DRF的Serializer也提供了一些方法了,
- 首先要写反序列化用的一些字段,有些字段要跟序列化区分开,
- Serializer提供了.is_valid()和.save()方法
一、Serializer
1、声明序列化类(app01中serializers.py)
from rest_framework import serializers from .models import Book class PublisherSerializer(serializers.Serializer): id = serializers.IntegerField() title = serializers.CharField(max_length=32) class AuthorSerializer(serializers.Serializer): id = serializers.IntegerField() name = serializers.CharField(max_length=32) book_obj = { "title": "Alex的使用教程", "w_category": 1, "pub_time": "2018-10-09", "publisher_id": 1, "author_list": [1, 2] } data = { "title": "Alex的使用教程2" } #3.1 验证器 validators 定义函数写验证规则 def my_validate(value): if "敏感信息" in value.lower(): raise serializers.ValidationError("不能含有敏感信息") else: return value class BookSerializer(serializers.Serializer): id = serializers.IntegerField(required=False) #required=False 不需要校验 title = serializers.CharField(max_length=32, validators=[my_validate]) #3.2 验证器 validators用自定义的验证方法,比validate_title权重高 CHOICES = ((1, "Python"), (2, "Go"), (3, "Linux")) category = serializers.ChoiceField(choices=CHOICES, source="get_category_display", read_only=True) #choices w_category = serializers.ChoiceField(choices=CHOICES, write_only=True) pub_time = serializers.DateField() #外键关系的序列化 publisher = PublisherSerializer(read_only=True) #read_only=True只序列化的时候用 publisher_id = serializers.IntegerField(write_only=True) #ForeignKey #write_only=True只反序列化的时候用 #ManyToMany的序列化 author = AuthorSerializer(many=True, read_only=True) #ManyToManyField author_list = serializers.ListField(write_only=True) #新建 def create(self, validated_data): book = Book.objects.create(title=validated_data["title"], category=validated_data["w_category"], pub_time=validated_data["pub_time"], publisher_id=validated_data["publisher_id"]) book.author.add(*validated_data["author_list"]) return book #修改 def update(self, instance, validated_data): instance.title = validated_data.get("title", instance.title) instance.category = validated_data.get("w_category", instance.w_category) instance.pub_time = validated_data.get("pub_time", instance.pub_time) instance.publisher_id = validated_data.get("publisher_id", instance.publisher_id) if validated_data.get("author_list"): instance.author.set(validated_data["author_list"]) instance.save() return instance #验证 #1、单个字段的验证 def validate_title(self, value): if "python" not in value.lower(): raise serializers.ValidationError("标题必须含有python") return value #2、多个字段的验证 def validate(self, attrs): if attrs["w_category"] == 1 and attrs["publisher_id"] == 1: return attrs else: raise serializers.ValidationError("分类以及标题不符合要求") #3、验证器 validators
2、用声明的序列化类去序列化(app01中views.py)
from .models import Book from rest_framework.views import APIView from rest_framework.viewsets import GenericViewSet from rest_framework.response import Response from .serializers import BookSerializer class BookView(APIView): def get(self, request): # book_obj = Book.objects.first() # ret = BookSerializer(book_obj) book_list = Book.objects.all() ret = BookSerializer(book_list, many=True) #多个ManyToManyField return Response(ret.data) def post(self, request): print(request.data) serializer = BookSerializer(data=request.data) if serializer.is_valid(): serializer.save() return Response(serializer.data) else: return Response(serializer.errors) class BookEditView(APIView): def get(self, request, id): book_obj = Book.objects.filter(id=id).first() ret = BookSerializer(book_obj) return Response(ret.data) def put(self, request, id): book_obj = Book.objects.filter(id=id).first() serializer = BookSerializer(book_obj, data=request.data, partial=True) #partial=True允许部分进行更新 if serializer.is_valid(): serializer.save() return Response(serializer.data) else: return Response(serializer.errors) def delete(self, request, id): book_obj = Book.objects.filter(id=id).first() book_obj.delete() return Response("")
二、ModelSerializer
- 跟模型紧密相关的序列化器
- 根据模型自动生成一组字段
- 默认实现了.update()以及.create()方法
1、定义一个ModelSerializer序列化器(app01中serializers.py)
from rest_framework import serializers from .models import Book # 验证器 validators 定义函数写验证规则 def my_validate(value): if "敏感信息" in value.lower(): raise serializers.ValidationError("不能含有敏感信息") else: return value class BookSerializer(serializers.ModelSerializer): #category = serializers.CharField(source="get_category_display",read_only=True) #自定制 category_display = serializers.SerializerMethodField(read_only=True) # SerializerMethodField publisher_info = serializers.SerializerMethodField(read_only=True) authors = serializers.SerializerMethodField(read_only=True) def get_category_display(self, obj): return obj.get_category_display() def get_authors(self, obj): # obj是当前序列化的book对象 #外键关联的对象有很多字段是用不到的~都传给前端会有数据冗余~就需要去定制序列化外键对象的哪些字段~~ authors_query_set = obj.author.all() return [{"id": author_obj.id, "name": author_obj.name} for author_obj in authors_query_set] def get_publisher_info(self, obj): # obj 是我们序列化的每个Book对象 # 外键关联的对象有很多字段是用不到的~都传给前端会有数据冗余~就需要去定制序列化外键对象的哪些字段~~ publisher_obj = obj.publisher return {"id": publisher_obj.id, "title": publisher_obj.title} class Meta: model = Book # fields = ["id", "title", "pub_time"] # exclude = ["user"] # 包含某些字段 排除某些字段 fields = "__all__" # depth = 1 #depth 代表找嵌套关系的第1层 #注意:当序列化类MATE中定义了depth时,这个序列化类中引用字段(外键)则自动变为只读 #read_only_fields = ["id", "category_display", "publisher_info", "authors"] extra_kwargs = {"category": {"write_only": True}, "publisher": {"write_only": True}, "author": {"write_only": True},"title": {"validators": [my_validate,]}}
from rest_framework import serializers from api import models class CourseModelSerializer(serializers.ModelSerializer): # price = serializers.SerializerMethodField() # learn_num = serializers.SerializerMethodField() learn_num = serializers.IntegerField(source='order_details.count') course_detail_id = serializers.IntegerField(source='coursedetail.id') # def get_price(self, obj): # # 把所有课程永久有效的价格拿出来 # price_obj = obj.price_policy.all().filter(valid_period=999).first() # return price_obj.price # def get_learn_num(self, obj): # return obj.order_details.count() # 修改序列化结果的终极方法 def to_representation(self, instance): # 调用父类的同名方法把序列化的结果拿到 data = super().to_representation(instance) # 针对序列化的结果做一些自定制操作 # 判断当前这个课程是否有永久有效的价格 price_obj = instance.price_policy.all().filter(valid_period=999).first() if price_obj: # 有永久有效的价格 data['has_price'] = True data['price'] = price_obj.price else: # 没有永久有效的价格策略 data['has_price'] = False return data class Meta: model = models.Course fields = '__all__' class CourseCategoryModelSerializer(serializers.ModelSerializer): class Meta: model = models.CourseCategory fields = '__all__'
三、Serializer和ModelSerializer的区别
四、JsonResponse和Django序列化
#app01中models.py from django.db import models __all__ = ["Book", "Publisher", "Author"] class Book(models.Model): title = models.CharField(max_length=32, verbose_name="图书名称") CHOICES = ((1, "Python"), (2, "Go"), (3, "Linux")) category = models.IntegerField(choices=CHOICES, verbose_name="图书的类别") pub_time = models.DateField(verbose_name="图书的出版日期") publisher = models.ForeignKey(to="Publisher", on_delete=None) author = models.ManyToManyField(to="Author") def __str__(self): return self.title class Meta: verbose_name_plural = "01-图书表" db_table = verbose_name_plural class Publisher(models.Model): title = models.CharField(max_length=32, verbose_name="出版社的名称") def __str__(self): return self.title class Meta: verbose_name_plural = "02-出版社表" db_table = verbose_name_plural class Author(models.Model): name = models.CharField(max_length=32, verbose_name="作者的姓名") def __str__(self): return self.name class Meta: verbose_name_plural = "03-作者表" db_table = verbose_name_plural #DRFDemo中urls.py from django.contrib import admin from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), path('books/', include("SerDemo.urls")), ] #app01中urls.py from django.urls import path, include from .views import BookView, BookEditView urlpatterns = [ path('list', BookView.as_view()), path('retrieve/<int:id>', BookEditView.as_view()), ]
#app01/views.py from django.views import View from django.http import HttpResponse, JsonResponse from django.core import serializers from .models import Book, Publisher class BookView(View): #第一版 用.values JsonResponse实现序列化 def get(self, request): book_list = Book.objects.values("id", "title", "category", "pub_time", "publisher") book_list = list(book_list) # 如果需要取外键关联的字段信息 需要循环获取外键 再去数据库查然后拼接成想要的 ret = [] for book in book_list: publisher_id = book["publisher"] publisher_obj = Publisher.objects.filter(id=publisher_id).first() book["publisher"] = { "id": publisher_id, "title": publisher_obj.title } ret.append(book) # ret = json.dumps(book_list, ensure_ascii=False) # return HttpResponse(ret) #时间 return JsonResponse(ret, safe=False, json_dumps_params={"ensure_ascii": False}) #第二版 用django serializers实现序列化 # 能够得到要的效果,但是结构有点复杂,而且choices不能显示对应的文本 def get(self, request): book_list = Book.objects.all() ret = serializers.serialize("json", book_list, ensure_ascii=False) return HttpResponse(ret)
DRF的视图
- 在Django REST Framework中内置的Request类扩展了Django中的Request类,实现了很多方便的功能--如请求数据解析和认证等。
- 比如,区别于Django中的request从request.GET中获取URL参数,从request.POST中取某些情况下的POST数据。
- 在APIView中封装的request,就实现了请求数据的解析:
- 对于GET请求的参数通过request.query_params来获取。
- 对于POST请求、PUT请求的数据通过request.data来获取。
一、源码查找
- django中写CBV的时候继承的是View,rest_framework继承的是APIView
- 不管是View还是APIView最开始调用的都是as_view()方法。
- APIView继承了View, 并且执行了View中的as_view()方法,最后用csrf_exempt()方法包裹view把view返回了,用csrf_exempt()方法包裹后去掉了csrf的认证。
- 在View中的as_view方法返回了view函数,而view函数执行了self.dispatch()方法,但是这里是APIView调用的,所以先从APIView找dispatch()方法。
- APIView的dispatch()方法中给request重新赋值了
- 去initialize_request中看下把什么赋值给了request,并且赋值给了self.request, 也就是视图中用的request.xxx到底是什么
- 可以看到,这个方法返回的是Request这个类的实例对象,注意看下这个Request类中的第一个参数request,是走django的时候的原来的request
- 去Request这个类里看 这个Request类把原来的request赋值给了self._request, 也就是说以后_request是老的request,新的request是这个Request类
- request.query_params 存放的是get请求的参数 request.data 存放的是所有的数据,包括post请求的以及put,patch请求
- 相比原来的django的request,现在的request更加精简,清晰了
- 框架提供了一个路由传参的方法ViewSetMixin
#1.徒手垒代码阶段 class SchoolView(APIView): def get(self, request, *args, **kwargs): query_set = models.School.objects.all() ser_obj = app01_serializers.SchoolSerializer(query_set, many=True) return Response(ser_obj.data) class SchoolDetail(APIView): def get(self, request, pk, *args, **kwargs): obj = models.School.objects.filter(pk=pk).first() ser_obj = app01_serializers.SchoolSerializer(obj) return Response(ser_obj.data) #路由 url(r'school/$', views.SchoolView.as_view()), url(r'school/(?P<pk>\d+)/$', views.SchoolDetail.as_view()), #2.使用混合类阶段 class SchoolView(GenericAPIView, mixins.ListModelMixin): queryset = models.School.objects.all() serializer_class = app01_serializers.SchoolSerializer def get(self, request, *args, **kwargs): return self.list(request, *args, **kwargs) class SchoolDetail(GenericAPIView, mixins.RetrieveModelMixin, mixins.CreateModelMixin): queryset = models.School.objects.all() serializer_class = app01_serializers.SchoolSerializer def get(self, request, pk, *args, **kwargs): return self.retrieve(request, pk, *args, **kwargs) def post(self, request, *args, **kwargs): return self.create(request, *args, **kwargs) #路由 url(r'school/$', views.SchoolView.as_view()), url(r'school/(?P<pk>\d+)/$', views.SchoolDetail.as_view()), # 3.使用通用类 class SchoolView(ListCreateAPIView): queryset = models.School.objects.all() serializer_class = app01_serializers.SchoolSerializer class SchoolDetail(RetrieveUpdateDestroyAPIView): queryset = models.School.objects.all() serializer_class = app01_serializers.SchoolSerializer #路由 url(r'school/$', views.SchoolView.as_view()), url(r'school/(?P<pk>\d+)/$', views.SchoolDetail.as_view()), #4.使用视图集 class SchoolView(ModelViewSet): queryset = models.School.objects.all() serializer_class = app01_serializers.SchoolSerializer #路由: url(r'school/$', views.SchoolView.as_view({ "get": "list", "post": "create", })), url(r'school/(?P<pk>\d+)/$', views.SchoolView.as_view({ 'get': 'retrieve', 'put': 'update', 'patch': 'partial_update', 'delete': 'destroy' })), #高级路由 from rest_framework.routers import DefaultRouter router = DefaultRouter() router.register(r'school', views.SchoolView) urlpatterns += router.urls
二、ModelViewSet
from django.urls import path, include from .views import BookView, BookEditView, BookModelViewSet urlpatterns = [ # path('list', BookView.as_view()), # path('retrieve/<int:id>', BookEditView.as_view()), path('list', BookModelViewSet.as_view({"get": "list", "post": "create"})), path('retrieve/<int:pk>', BookModelViewSet.as_view({"get": "retrieve", "put": "update", "delete": "destroy"})), ]
from .models import Book from .serializers import BookSerializer from rest_framework.viewsets import ModelViewSet class BookModelViewSet(ModelViewSet): queryset = Book.objects.all() serializer_class = BookSerializer #现在的视图就只要写两行就可以了 #注意:用框架封装的视图~url上的那个关键字参数要用pk系统默认的 ##path('retrieve/<int:pk>', BookModelViewSet.as_view({"get": "retrieve", "put": "update", "delete": "destroy"})) 用pk # from rest_framework import views # from rest_framework import generics # from rest_framework import mixins # from rest_framework import viewsets
from .models import Book from rest_framework.views import APIView from rest_framework.response import Response from .serializers import BookSerializer class GenericAPIView(APIView): query_set = None serializer_class = None def get_queryset(self): return self.query_set def get_serializer(self, *args, **kwargs): return self.serializer_class(*args, **kwargs) class ListModelMixin(object): def list(self, request): queryset = self.get_queryset() ret = self.get_serializer(queryset, many=True) return Response(ret.data) class CreateModelMixin(object): def create(self, request): serializer = self.get_serializer(data=request.data) if serializer.is_valid(): serializer.save() return Response(serializer.data) else: return Response(serializer.errors) class RetrieveModelMixin(object): def retrieve(self, request, id): book_obj = self.get_queryset().filter(id=id).first() ret = self.get_serializer(book_obj) return Response(ret.data) class UpdateModelMixin(object): def update(self, request, id): book_obj = self.get_queryset().filter(id=id).first() serializer = self.get_serializer(book_obj, data=request.data, partial=True) if serializer.is_valid(): serializer.save() return Response(serializer.data) else: return Response(serializer.errors) class DestroyModelMixin(object): def destroy(self, request, id): book_obj = self.get_queryset().filter(id=id).first() book_obj.delete() return Response("") class ListCreateAPIView(GenericAPIView, ListModelMixin, CreateModelMixin): pass class RetrieveUpdateDestroyAPIView(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin): pass # class BookView(GenericAPIView, ListModelMixin, CreateModelMixin): class BookView(ListCreateAPIView): query_set = Book.objects.all() serializer_class = BookSerializer def get(self, request): # book_obj = Book.objects.first() # ret = BookSerializer(book_obj) # book_list = Book.objects.all() # book_list = self.get_queryset() # ret = self.get_serializer(book_list, many=True) # return Response(ret.data) return self.list(request) def post(self, request): # print(request.data) # serializer = BookSerializer(data=request.data) # if serializer.is_valid(): # serializer.save() # return Response(serializer.data) # else: # return Response(serializer.errors) return self.create(request) # class BookEditView(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin): class BookEditView(RetrieveUpdateDestroyAPIView): query_set = Book.objects.all() serializer_class = BookSerializer def get(self, request, id): # book_obj = Book.objects.filter(id=id).first() # ret = BookSerializer(book_obj) # return Response(ret.data) return self.retrieve(request, id) def put(self, request, id): # book_obj = Book.objects.filter(id=id).first() # serializer = BookSerializer(book_obj, data=request.data, partial=True) # if serializer.is_valid(): # serializer.save() # return Response(serializer.data) # else: # return Response(serializer.errors) return self.update(request, id) def delete(self, request, id): # book_obj = Book.objects.filter(id=id).first() # book_obj.delete() # return Response("") return self.destroy(request, id) # class ViewSetMixin(object): # def as_view(self): # """ # 按照我们参数指定的去匹配 # get-->list # :return: # """ from rest_framework.viewsets import ViewSetMixin #必须继承ViewSetMixin,路由的as_view方法才可以传参 class ModelViewSet(ViewSetMixin, GenericAPIView, ListModelMixin, CreateModelMixin, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin): pass #上面封装的所有框架都帮我们封装好了 #from rest_framework.viewsets import ModelViewSet #注意:用框架封装的视图url上的那个关键字参数要用pk系统默认的 class BookModelViewSet(ModelViewSet): queryset = Book.objects.all() serializer_class = BookSerializer
from rest_framework import views from rest_framework import generics from rest_framework import mixins from rest_framework import viewsets from bms import models from rest_framework.response import Response from rest_framework.views import APIView from bms.modelserializers import BookModelSerializer, PublisherModelSerializer, AuthorModelSerializer class BookListView(APIView): def get(self, request): # 1. 查出所有的书籍信息 queryset = models.Book.objects.all() # [Book_obj1, Book_obj2, ...] # 2. 使用serizlizer序列化 ser_obj = BookModelSerializer(queryset, many=True) # [obj1, obj2, obj3, ...] return Response(ser_obj.data) # def post(self, request): # 2. 对数据做有效性校验 ser_obj = BookModelSerializer(data=request.data) if ser_obj.is_valid(): ser_obj.save() # 调用的是BookSerializer类中的create方法,需要自己去实现 # 3. 拿到序列化的数据去数据库创建新记录 return Response("ok") else: return Response(ser_obj.errors) class BookDetailView(APIView): """这是书籍详情相关的接口 支持:GET/PUT/DELETE""" def get(self, request, pk): """获取具体某本书的信息""" # 1. 根据pk去查询具体的那本书籍对象 book_obj = models.Book.objects.filter(pk=pk).first() if book_obj: # 2. 将书籍对象 序列化成 json格式的数据 ser_obj = BookModelSerializer(book_obj) # 3. 返回响应 return Response(ser_obj.data) else: return Response("无效的书籍id") def put(self, request, pk): """修改具体某一本书""" # 1. 根据pk去查询具体的那本书籍对象 book_obj = models.Book.objects.filter(pk=pk).first() if book_obj: # 2. 获取用户 发送过来的数据并且更新对象 ser_obj = BookModelSerializer(instance=book_obj, data=request.data, partial=True) # form组件中也有类似的实现 if ser_obj.is_valid(): # 3. 保存并返回修改后的数据 ser_obj.save() return Response(ser_obj.data) else: return Response(ser_obj.errors) else: return Response("无效的书籍id") def delete(self, request, pk): """删除具体某一本书""" # 1. 根据pk去查询具体的那本书籍对象 book_obj = models.Book.objects.filter(pk=pk) if book_obj: # 删除书籍对象 book_obj.delete() return Response("删除成功") else: return Response("无效的书籍id") #####################进化 class GenericAPIView(APIView): """把视图中可能用到的配置和方法封装起来""" queryset = None serializer_class = None def get_queryset(self, request, *args, **kwargs): ######### 让每次请求来的时候都现查一次数据 return self.queryset.all() def get_object(self, request, pk, *args, **kwargs): return self.get_queryset(request, *args, **kwargs).filter(pk=pk).first() # python mixin(混合类):不能单独使用,和其它类搭配起来使用(利用了Python支持多继承) class ListModelMixin(object): def list(self, request, *args, **kwargs): ##get queryset = self.get_queryset() ser_obj = self.serializer_class(queryset, many=True) return Response(ser_obj.data) class CreateModelMixin(object): def create(self, request, *args, **kwargs): ##post ser_obj = self.serializer_class(data=request.data) if ser_obj.is_valid(): ser_obj.save() return Response("ok") else: return Response(ser_obj.errors) # 获取具体某一条记录 class RetrieveModelMixin(object): def retrieve(self, request, pk, *args, **kwargs): ##get obj = self.get_object(request, pk, *args, **kwargs) if obj: # 2. 将书籍对象 序列化成 json格式的数据 ser_obj = self.serializer_class(obj) # 3. 返回响应 return Response(ser_obj.data) else: return Response("无效的id") class UpdateModelMixin(object): def update(self, request, pk, *args, **kwargs): ##put obj = self.get_object(request, pk, *args, **kwargs) if obj: ser_obj = self.serializer_class(instance=obj, data=request.data, partial=True) # form组件中也有类似的实现 if ser_obj.is_valid(): ser_obj.save() return Response(ser_obj.data) else: return Response(ser_obj.errors) else: return Response("无效的id") class DestroyModelMixin(object): def destroy(self, request, pk, *args, **kwargs): ##delete obj = self.get_object(request, pk, *args, **kwargs) if obj: obj.delete() return Response("删除成功") else: return Response("无效的id") # 出版社 class PublisherListView(GenericAPIView, ListModelMixin, CreateModelMixin): queryset = models.Publisher.objects.all() serializer_class = PublisherModelSerializer def get(self, request, *args, **kwargs): return self.list(request, *args, **kwargs) def post(self, request, *args, **kwargs): return self.create(request, *args, **kwargs) class PublisherDetailView(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin): queryset = models.Publisher.objects.all() serializer_class = PublisherModelSerializer def get(self, request, pk, *args, **kwargs): return self.retrieve(request, pk, *args, **kwargs) def delete(self, request, pk, *args, **kwargs): return self.destroy(request, pk, *args, **kwargs) def put(self, request, pk, *args, **kwargs): return self.update(request, pk, *args, **kwargs) #####################超进化##两个视图 class CreateAPIView(CreateModelMixin,GenericAPIView): def post(self, request, *args, **kwargs): return self.create(request, *args, **kwargs) class ListAPIView(ListModelMixin,GenericAPIView): def get(self, request, *args, **kwargs): return self.list(request, *args, **kwargs) class RetrieveAPIView(RetrieveModelMixin,GenericAPIView): def get(self, request, pk, *args, **kwargs): return self.retrieve(request, pk, *args, **kwargs) class DestroyAPIView(DestroyModelMixin,GenericAPIView): def delete(self, request, pk, *args, **kwargs): return self.destroy(request, pk, *args, **kwargs) class UpdateAPIView(UpdateModelMixin,GenericAPIView): def put(self, request, pk, *args, **kwargs): return self.update(request, pk, *args, **kwargs) # 作者 class AuthorListView(ListAPIView,CreateAPIView): queryset = models.Author.objects.all() serializer_class = AuthorModelSerializer class AuthorDetailView(RetrieveAPIView,DestroyAPIView,UpdateAPIView): queryset = models.Author.objects.all() serializer_class = AuthorModelSerializer ##########最终######一个视图搞定####必须继承ViewSetMixin,路由的as_view方法才可以传参 ##配合url##actions urlpatterns = [ # url(r'publishers/$', views.PublisherListView.as_view()), # 出版社列表 # url(r'publishers/(?P<pk>\d+)/$', views.PublisherDetailView.as_view()), # 出版社详情 # # url(r'authors/$', views.AuthorListView.as_view()), # 作者列表 # url(r'authors/(?P<pk>\d+)/$', views.AuthorDetailView.as_view()), # 作者详情 url(r'authors/$', views.AuthorViewSet.as_view(actions={'get': 'list', 'post': 'create'})), # 作者列表 url(r'authors/(?P<pk>\d+)/$', views.AuthorViewSet.as_view( actions={'get': 'retrieve', 'put': 'update', 'delete': 'destroy'}) ), # 作者详情 ] # 或者 from rest_framework.routers import DefaultRouter router = DefaultRouter() router.register('authors', views.AuthorViewSet) urlpatterns += router.urls ########## from rest_framework.viewsets import GenericViewSet class ViewSetMixin(object): def as_view(cls, actions=None, **initkwargs): # ... pass class GenericViewSet(ViewSetMixin, GenericAPIView): pass class AuthorViewSet(GenericViewSet,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin,ListModelMixin): """ list() create() retrieve() update() destroy() """ queryset = models.Author.objects.all() serializer_class = AuthorModelSerializer #########################rest_framework############################### class ModelViewSet(mixins.CreateModelMixin, mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, mixins.ListModelMixin, GenericViewSet): pass from rest_framework.viewsets import ModelViewSet class AuthorViewSet(ModelViewSet): """ list() create() retrieve() update() destroy() """ queryset = models.Author.objects.all() serializer_class = AuthorModelSerializer
DRF的路由
from django.urls import path, include from .views import BookView, BookEditView, BookModelViewSet from rest_framework.routers import DefaultRouter router = DefaultRouter() router.register(r"book", BookModelViewSet) urlpatterns = [ # path('list', BookView.as_view()), # path('retrieve/<int:id>', BookEditView.as_view()), #path('list', BookModelViewSet.as_view({"get": "list", "post": "create"})), #path('retrieve/<int:pk>', BookModelViewSet.as_view({"get": "retrieve", "put": "update", "delete": "destroy"})), ] urlpatterns += router.urls #通过框架可以把路由视图都变的非常简单 #但是需要自定制的时候还是需要用APIView写,当不需要那么多路由的时候,也不要用这种路由注册.
DRF的版本
随着项目的更新,版本就越来越多,不可能新的版本出了,以前旧的版本就不进行维护了,就需要对版本进行控制了
一、源码查找
- APIView返回View中的view函数,然后调用dispatch方法,APIView的dispatch方法
- 执行self.initial方法之前是各种赋值,包括request的重新封装赋值
def dispatch(self, request, *args, **kwargs): """ `.dispatch()` is pretty much the same as Django's regular dispatch, but with extra hooks for startup, finalize, and exception handling. """ self.args = args self.kwargs = kwargs request = self.initialize_request(request, *args, **kwargs) self.request = request self.headers = self.default_response_headers # deprecate? try: self.initial(request, *args, **kwargs) ##### ... def initial(self, request, *args, **kwargs): """ Runs anything that needs to occur prior to calling the method handler. """ self.format_kwarg = self.get_format_suffix(**kwargs) # Perform content negotiation and store the accepted info on the request neg = self.perform_content_negotiation(request) request.accepted_renderer, request.accepted_media_type = neg # Determine the API version, if versioning is in use. # 版本控制 # self.determine_version 这个方法是找我们自己定义的版本控制类,没有的话返回(None,None) version, scheme = self.determine_version(request, *args, **kwargs) request.version, request.versioning_scheme = version, scheme ###version版本信息赋值给了 request.version 版本控制方案赋值给了 request.versioning_scheme ###其实这个版本控制方案~就是我们配置的版本控制的类,也就是说,APIView通过这个方法初始化自己提供的组件 ###接下来看看框架提供了哪些版本的控制方法在rest_framework.versioning里,from rest_framework import versioning # Ensure that the incoming request is permitted # 认证 权限 频率组件 self.perform_authentication(request) self.check_permissions(request) self.check_throttles(request) ... def determine_version(self, request, *args, **kwargs): """ If versioning is being used, then determine any API version for the incoming request. Returns a two-tuple of (version, versioning_scheme) """ if self.versioning_class is None: return (None, None) #scheme是我们配置的版本控制类的实例化对象 scheme = self.versioning_class() #返回值scheme.determine_version MyVersion中必须定义determine_version这个方法,从上面可以看出此方法返回版本号version return (scheme.determine_version(request, *args, **kwargs), scheme)
二、使用方法1(URL上携带版本信息的配置)
第1步:settings.py
REST_FRAMEWORK = { # 默认使用的版本控制类 'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning', # 允许的版本 'ALLOWED_VERSIONS': ['v1', 'v2'], # 版本使用的参数名称 'VERSION_PARAM': 'version', # 默认使用的版本 'DEFAULT_VERSION': 'v1', }
第2步:app01中urls.py
urlpatterns = [ url(r"^(?P<version>[v1|v2]+)/test01", TestView.as_view()), ]
第3步:测试视图app01.views.py
class TestView(APIView): def get(self, request, *args, **kwargs): print(request.versioning_scheme) ret = request.version if ret == "v1": return Response("版本v1的信息") elif ret == "v2": return Response("版本v2的信息") else: return Response("根本就匹配不到这个路由")
三、使用方法2(URL过滤条件配置版本信息)
第1步:settings.py
REST_FRAMEWORK = { "DEFAULT_VERSIONING_CLASS": "utils.version.MyVersion", # "DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.QueryParameterVersioning", "DEFAULT_VERSION": "v1", "ALLOWED_VERSIONS": ['v1', 'v2'], "VERSION_PARAM": "version" }
第2步:app01中urls.py
from django.urls import path, include from .views import DemoView urlpatterns = [ path(r"", DemoView.as_view()), ]
第3步:utils中version.py
from rest_framework import versioning class MyVersion(object): def determine_version(self, request, *args, **kwargs): # 返回值 给了request.version # 返回版本号 # 版本号携带在过滤条件 xxxx?version=v1中,版本号在那就去那取值 version = request.query_params.get("version", "v1") return version
第4步:测试视图app01.views.py
from rest_framework.views import APIView from rest_framework.response import Response class DemoView(APIView): def get(self, request): print(request.version) print(request.versioning_scheme) # 得到版本号 根据版本号的不同返回不同的信息 if request.version == "v1": return Response("v1版本的数据") elif request.version == "v2": return Response("v2版本的数据") return Response("不存在的版本")
DRF的认证
每次给服务器发请求,由于Http的无状态,导致每次都是新的请求,
服务端需要对每次来的请求进行认证,看用户是否登录,以及登录用户是谁,
服务器对每个请求进行认证的时候,不可能在每个视图函数中都写认证,
一定是把认证逻辑抽离出来,以前我们可能会加装饰器或者中间件。
一、源码查找
def dispatch(self, request, *args, **kwargs): """ `.dispatch()` is pretty much the same as Django's regular dispatch, but with extra hooks for startup, finalize, and exception handling. """ self.args = args self.kwargs = kwargs request = self.initialize_request(request, *args, **kwargs) self.request = request self.headers = self.default_response_headers # deprecate? try: self.initial(request, *args, **kwargs) # Get the appropriate handler method if request.method.lower() in self.http_method_names: handler = getattr(self, request.method.lower(), self.http_method_not_allowed) else: handler = self.http_method_not_allowed response = handler(request, *args, **kwargs) except Exception as exc: response = self.handle_exception(exc) self.response = self.finalize_response(request, response, *args, **kwargs) return self.response ... def initialize_request(self, request, *args, **kwargs): """ Returns the initial request object. """ parser_context = self.get_parser_context(request) return Request( request, parsers=self.get_parsers(), authenticators=self.get_authenticators(), negotiator=self.get_content_negotiator(), parser_context=parser_context ) def initial(self, request, *args, **kwargs): """ Runs anything that needs to occur prior to calling the method handler. """ self.format_kwarg = self.get_format_suffix(**kwargs) # Perform content negotiation and store the accepted info on the request neg = self.perform_content_negotiation(request) request.accepted_renderer, request.accepted_media_type = neg # Determine the API version, if versioning is in use. # 版本控制 # self.determine_version 这个方法是找我们自己定义的版本控制类,没有的话返回(None,None) version, scheme = self.determine_version(request, *args, **kwargs) request.version, request.versioning_scheme = version, scheme # Ensure that the incoming request is permitted # 认证 权限 频率组件 self.perform_authentication(request) self.check_permissions(request) self.check_throttles(request) ... def perform_authentication(self, request): """ Perform authentication on the incoming request. Note that if you override this and simply 'pass', then authentication will instead be performed lazily, the first time either `request.user` or `request.auth` is accessed. """ request.user ... #去类Request中找user @property def user(self): """ Returns the user associated with the current request, as authenticated by the authentication classes provided to the request. """ if not hasattr(self, '_user'): with wrap_attributeerrors(): #__enter__ self._authenticate() #__exit__ return self._user ... def _authenticate(self): """ Attempt to authenticate the request using each authentication instance in turn. """ #这里的authentications是最开始实例化Request类的时候传过来的 #是调用get_authenticators这个方法, # 这个方法的返回值是 return [auth() for auth in self,authentication_classes] #authentication_classes如果我们配置了就用我们配置的,否则就从默认配置文件中读取配置类 #返回的auth()是认证类实例化后的 for authenticator in self.authenticators: #查看authenticators try: #也就是说这里的authenticator是认证类实例化后的 #authenticate方法是我们必须去实现的方法 #authenticate的参数self,我们是通过新的request.user进来的,所以这个self就是新的request user_auth_tuple = authenticator.authenticate(self) except exceptions.APIException: self._not_authenticated() raise if user_auth_tuple is not None: self._authenticator = authenticator #request.user #request.auth self.user, self.auth = user_auth_tuple return self._not_authenticated() ... class Request(object): def __init__(self, request, parsers=None, authenticators=None, negotiator=None, parser_context=None): assert isinstance(request, HttpRequest), ( 'The `request` argument must be an instance of ' '`django.http.HttpRequest`, not `{}.{}`.' .format(request.__class__.__module__, request.__class__.__name__) ) self._request = request self.parsers = parsers or () self.authenticators = authenticators or () ## authenticators看传参了么 ... def initialize_request(self, request, *args, **kwargs): """ Returns the initial request object. """ parser_context = self.get_parser_context(request) return Request( request, parsers=self.get_parsers(), authenticators=self.get_authenticators(), #传参了 negotiator=self.get_content_negotiator(), parser_context=parser_context ) ... def get_authenticators(self): """ Instantiates and returns the list of authenticators that this view can use. """ #self.authentication_classes去配置文件拿所有的认证类 return [auth() for auth in self.authentication_classes]
- APIView的dispatch方法里给request重新赋值了
- APIView的dispatch方法里给执行了initial方法,初始化了版本认证,权限,频率组件,initial方法的参数request是重新赋值后的
- 权限组件返回的是request.user,initial的request是重新赋值之后的,所以这里的request是重新赋值之后的,也就是Request类实例对象, 那这个user一定是一个静态方法.
二、使用方法
1、app01中models.py
# 先在model中注册模型类 # 并且进行数据迁移 from django.db import models class User(models.Model): username = models.CharField(max_length=32) pwd = models.CharField(max_length=16) token = models.UUIDField()
2、app01中urls.py
from django.urls import path from .views import DemoView, LoginView, TestView urlpatterns = [ path(r"login", LoginView.as_view()), path(r"test", TestView.as_view()), ]
3、app01中views.py
import uuid from app01 import models from utils.auth import MyAuth from rest_framework.views import APIView from rest_framework.response import Response class LoginView(APIView): def post(self, request): username = request.data.get("username") pwd = request.data.get("pwd") if username and pwd: obj = models.User.objects.filter(username=username, pwd=pwd).first() if obj: token = uuid.uuid4() obj.token = token obj.save() return Response({'error_no': 0, 'token': token}) else: return Response("用户名密码错误") return Response("用户名密码不能为空") # 局部视图认证 class TestView(APIView): authentication_classes = [MyAuth, ] def get(self, request): print(request.user) print(request.auth) user_id = request.user.id return Response("认证测试")
4、utils中auth.py 写一个认证的类
from rest_framework.exceptions import AuthenticationFailed from app01.models import User from rest_framework.authentication import BaseAuthentication class MyAuth(BaseAuthentication): def authenticate(self, request): # 做认证 看他是否登录 # 拿到token,此处是从url过滤条件里拿到token # 去数据库看token是否合法 # 合法的token能够获取用户信息 token = request.query_params.get("token", "") if not token: raise AuthenticationFailed("没有携带token") user_obj = User.objects.filter(token=token).first() if not user_obj: raise AuthenticationFailed("token不合法") # return (None, None) return (user_obj, token) #第1个返回值是request.user 第2个返回值是request.auth
5、全局配置认证
REST_FRAMEWORK = { # "DEFAULT_VERSIONING_CLASS": "utils.version.MyVersion", "DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.QueryParameterVersioning", "DEFAULT_VERSION": "v1", "ALLOWED_VERSIONS": "v1, v2", "VERSION_PARAM": "ver", # "DEFAULT_AUTHENTICATION_CLASSES": ["utils.auth.MyAuth", ] #全局配置 }
DRF的权限
对某件事情决策的范围和程度叫做权限
一、源码查找
def check_permissions(self, request): """ Check if the request should be permitted. Raises an appropriate exception if the request is not permitted. """ for permission in self.get_permissions(): #permission我们写的权限类的实例对象 MyPermission() if not permission.has_permission(request, self): #permission_denied是抛出异常的 #也就是说我们的权限类中必须有has_permission这个方法,否则就抛出异常 self.permission_denied( #message 定义异常信息 request, message=getattr(permission, 'message', None) )
- 权限类一定要有has_permission方法,否则就会抛出异常,这也是框架提供的钩子
- rest_framework.permissions这个文件中存放了框架提供的所有权限的方法
- BasePermission 这个是写权限类继承的一个基础权限类
- Python代码是一行一行执行的,那么执行initial方法初始化这些组件的时候 也是有顺序的,版本在前面然后是认证,然后是权限最后是频率
- 版本,认证,权限,频率这几个组件的源码是一个流程
二、使用方法
1、app01中models.py
# 先在model中注册模型类 # 并且进行数据迁移 from django.db import models class User(models.Model): username = models.CharField(max_length=32) pwd = models.CharField(max_length=16) token = models.UUIDField() type = models.IntegerField(choices=((1, "vip"), (2, "vvip"), (3, "普通")), default=3)
2、app01中urls.py
from django.urls import path from .views import DemoView, LoginView, TestView urlpatterns = [ path(r"login", LoginView.as_view()), path(r"test", TestView.as_view()), ]
3、app01中views.py
import uuid from .models import User from utils.auth import MyAuth from utils.permission import MyPermission # Create your views here. from rest_framework.views import APIView from rest_framework.response import Response class DemoView(APIView): def get(self, request): return Response("认证demo~") class LoginView(APIView): def post(self, request): username = request.data.get("username") pwd = request.data.get("pwd") # 登录成功 生成token 会把token给你返回 token = uuid.uuid4() User.objects.create(username=username, pwd=pwd, token=token) return Response("创建用户成功") class TestView(APIView): authentication_classes = [MyAuth,] permission_classes = [MyPermission, ] #局部配置权限 def get(self, request): print(request.user) print(request.auth) user_id = request.user.id return Response("认证测试")
4、utils中permission.py 写一个权限类
from rest_framework.permissions import BasePermission class MyPermission(BasePermission): message = "您没有权限" def has_permission(self, request, view): # request.user:当前经过认证的用户对象, # 如果没有认证 request.user 就是匿名用户 if not request.user: # 认证没有通过 return False # 判断用户是否有权限 if request.user.type == 1: return True else: return False
5、全局配置权限
REST_FRAMEWORK = { # "DEFAULT_VERSIONING_CLASS": "utils.version.MyVersion", "DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.QueryParameterVersioning", "DEFAULT_VERSION": "v1", "ALLOWED_VERSIONS": "v1, v2", "VERSION_PARAM": "ver", # "DEFAULT_AUTHENTICATION_CLASSES": ["utils.auth.MyAuth", ] #全局配置 } REST_FRAMEWORK = { # "DEFAULT_VERSIONING_CLASS": "utils.version.MyVersion", # 默认使用的版本控制类 'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning', # 允许的版本 "ALLOWED_VERSIONS": "v1, v2", # 版本使用的参数名称 "VERSION_PARAM": "ver", # 默认使用的版本 'DEFAULT_VERSION': 'v1', # 配置全局认证 # "DEFAULT_AUTHENTICATION_CLASSES": ["utils.auth.MyAuth", ] #全局配置 # 配置全局权限 "DEFAULT_PERMISSION_CLASSES": ["utils.permission.MyPermission"] }
DRF的频率
开放平台的API接口调用需要限制其频率,以节约服务器资源和避免恶意的频繁调用。
一、源码查找
def check_throttles(self, request): """ Check if request should be throttled. Raises an appropriate exception if the request is throttled. """ #throttle 配置每个频率控制类的实例化对象 allow_request方法和wait方法 for throttle in self.get_throttles(): if not throttle.allow_request(request, self): self.throttled(request, throttle.wait()) ... def get_throttles(self): """ Instantiates and returns the list of throttles that this view uses. """ return [throttle() for throttle in self.throttle_classes]
二、频率组件原理
DRF中的频率控制基本原理是基于访问次数和时间的,当然也可以通过自己定义的方法来实现。 当请求进来,走到频率组件的时候,DRF内部会有一个字典来记录访问者的IP, 以这个访问者的IP为key,value为一个列表,存放访问者每次访问的时间, { IP1: [第三次访问时间,第二次访问时间,第一次访问时间],} 把每次访问最新时间放入列表的最前面,记录这样一个数据结构后,通过什么方式限流呢~~ 如果我们设置的是10秒内只能访问5次, -- 1,判断访问者的IP是否在这个请求IP的字典里 -- 2,保证这个列表里都是最近10秒内的访问的时间 判断当前请求时间和列表里最早的(也就是最后的)请求时间的查 如果差大于10秒,说明请求以及不是最近10秒内的,删除掉, 继续判断倒数第二个,直到差值小于10秒 -- 3,判断列表的长度(即访问次数),是否大于我们设置的5次, 如果大于就限流,否则放行,并把时间放入列表的最前面。
三、使用方法
1、app01中views.py
import uuid from .models import User from utils.auth import MyAuth from utils.permission import MyPermission from utils.throttle import MyThrottle # Create your views here. from rest_framework.views import APIView from rest_framework.response import Response class LoginView(APIView): def post(self, request): username = request.data.get("username") pwd = request.data.get("pwd") # 登录成功 生成token 会把token给你返回 token = uuid.uuid4() User.objects.create(username=username, pwd=pwd, token=token) return Response("创建用户成功") class TestView(APIView): authentication_classes = [MyAuth,] permission_classes = [MyPermission, ] throttle_classes = [MyThrottle, ] def get(self, request): print(request.user) print(request.auth) user_id = request.user.id return Response("认证测试")
2、utils中throttle.py 写一个频率类
from rest_framework.throttling import BaseThrottle, SimpleRateThrottle import time VISIT_RECORD = {} #自定义的频率限制类 # class MyThrottle(BaseThrottle): # # def __init__(self): # self.history = None # # def allow_request(self, request, view): # # 实现限流的逻辑 # # 以IP限流 # # 访问列表 {IP: [time1, time2, time3]} # # 1, 获取请求的IP地址 # ip = request.META.get("REMOTE_ADDR") # # 2,判断IP地址是否在访问列表 # now = time.time() # if ip not in VISIT_RECORD: # # --1, 不在 需要给访问列表添加key,value # VISIT_RECORD[ip] = [now,] # return True # # --2 在 需要把这个IP的访问记录 把当前时间加入到列表 # history = VISIT_RECORD[ip] # history.insert(0, now) # # 3, 确保列表里最新访问时间以及最老的访问时间差 是1分钟 # while history and history[0] - history[-1] > 60: # history.pop() # self.history = history # # 4,得到列表长度,判断是否是允许的次数 # if len(history) > 3: # return False # else: # return True # # def wait(self): # # 返回需要再等多久才能访问 # time = 60 - (self.history[0] - self.history[-1]) # return time #使用自带的频率限制类 class MyThrottle(SimpleRateThrottle): scope = "WD" def get_cache_key(self, request, view): # 如果以IP地址做限流返回IP地址 key = self.get_ident(request) return key
3、全局配置频率
REST_FRAMEWORK = { # "DEFAULT_VERSIONING_CLASS": "utils.version.MyVersion", "DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.QueryParameterVersioning", "DEFAULT_VERSION": "v1", "ALLOWED_VERSIONS": "v1, v2", "VERSION_PARAM": "ver", # "DEFAULT_AUTHENTICATION_CLASSES": ["utils.auth.MyAuth", ] #全局配置 } REST_FRAMEWORK = { # "DEFAULT_VERSIONING_CLASS": "utils.version.MyVersion", # 默认使用的版本控制类 'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning', # 允许的版本 "ALLOWED_VERSIONS": "v1, v2", # 版本使用的参数名称 "VERSION_PARAM": "ver", # 默认使用的版本 'DEFAULT_VERSION': 'v1', # 配置全局认证 # "DEFAULT_AUTHENTICATION_CLASSES": ["utils.auth.MyAuth", ] #全局配置 # 配置全局权限 "DEFAULT_PERMISSION_CLASSES": ["utils.permission.MyPermission"], # 配置自定义频率限制 "DEFAULT_THROTTLE_CLASSES": ["Throttle.throttle.MyThrottle"], # 配置频率限制 "DEFAULT_THROTTLE_RATES": { "WD": "3/m" #速率配置每分钟不能超过3次访问,WD是scope定义的值, } }
DRF的分页组件
- DRF提供的三种分页:
from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination, CursorPagination
- 全局配置:
REST_FRAMEWORK = { 'PAGE_SIZE': 2 }
- 第1种 PageNumberPagination 看第n页,每页显示n条数据
http://127.0.0.1:8000/book?page=2&size=1
- 第2种 LimitOffsetPagination 在第n个位置 向后查看n条数据
http://127.0.0.1:8000/book?offset=2&limit=1
- 第3种 CursorPagination 加密游标的分页 把上一页和下一页的id记住
http://127.0.0.1:8000/book?page=2&size=1
一、utils中pagination.py(自定义分页类)
from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination, CursorPagination # class MyPagination(PageNumberPagination): # # xxxx?page=1&size=2 # page_size = 1 # 每页显示多少条 # page_query_param = "page" # URL中页码的参数 # page_size_query_param = "size" # URL中每页显示条数的参数 # max_page_size = 3 # 最大页码数限制 # class MyPagination(LimitOffsetPagination): # # xxxx?offset=120&limit=20 #从第120条开始往后查20条数据 # default_limit = 1 # limit_query_param = "limit" # offset_query_param = "offset" # max_limit = 3 class MyPagination(CursorPagination): # cursor_query_param = "cursor" page_size = 2 ordering = "-id" #
二、app01中views.py
from django.shortcuts import render from rest_framework.views import APIView from rest_framework.response import Response from SerDemo.models import Book from SerDemo.serializers import BookSerializer # Create your views here. from rest_framework import pagination from utils.pagination import MyPagination from rest_framework.generics import GenericAPIView from rest_framework.mixins import ListModelMixin # class BookView(APIView): # # def get(self, request): # queryset = Book.objects.all() # # 1,实例化分页器对象 # page_obj = MyPagination() # # 2,调用分页方法去分页queryset # page_queryset = page_obj.paginate_queryset(queryset, request, view=self) # # 3,把分页好的数据序列化返回 # # 4, 带着上一页下一页连接的响应 # ser_obj = BookSerializer(page_queryset, many=True) # # 返回带超链接 需返回的时候用内置的响应方法 # return page_obj.get_paginated_response(ser_obj.data) class BookView(GenericAPIView, ListModelMixin): queryset = Book.objects.all() serializer_class = BookSerializer pagination_class = MyPagination # self.paginate_queryset(queryset) def get(self, request): return self.list(request)
三、全局配置
REST_FRAMEWORK = { 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination', 'PAGE_SIZE': 20 #每页显示的数量 }
DRF的解析器
- 解析器的作用就是服务端接收客户端传过来的数据,把数据解析成自己想要的数据类型的过程。本质就是对请求体中的数据进行解析。
- 在了解解析器之前要先知道Accept以及ContentType请求头。
- Accept是告诉对方能解析什么样的数据,通常也可以表示想要什么样的数据。
- ContentType是告诉对方我给你的是什么样的数据类型。
- 解析器工作原理的本质 就是拿到请求的ContentType来判断前端给后端数据类型是什么,然后后端去拿相应的解析器去解析数据。
-
from django.core.handlers.wsgi import WSGIRequest
一、Django的解析器
- 请求进来请求体中的数据在request.body中,那也就证明,解析器会把解析好的数据放入request.body
- 在视图中可以打印request的类型,能够知道request是WSGIRequest这个类。
- application/x-www-form-urlencoded不是不能上传文件,是只能上传文本格式的文件
- multipart/form-data是将文件以二进制的形式上传,这样可以实现多种类型的文件上传 一个解析到request.POST, request.FILES中。
- 也就是说之前能在request中能到的各种数据是因为用了不同格式的数据解析器
- Django只能解析cont_type=multipart/form-data 和cont_type=application/x-www-form-urlencoded的数据,不能解析json
二、DRF的解析器
- 在request.data拿数据的时候解析器会被调用
四、DRF的解析器使用方法
1、app01中views.py
from django.shortcuts import render from django.views import View from django.http import HttpResponse from django.core.handlers.wsgi import WSGIRequest from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.negotiation import DefaultContentNegotiation from rest_framework import parsers # Create your views here. class DjangoView(View): def get(self, request): print(type(request)) # Request # request.GET # request.POST # json request.body return HttpResponse("django解析器测试~~") class DRFView(APIView): #parser_classes = [parsers.JSONParser, ] #一般不配置 def get(self, request): # request 重新封装的request Request # request.data # return Response("DRF解析器的测试~~")
DRF的渲染器
渲染器就是友好的展示数据,我们在浏览器中展示的DRF测试的那个页面就是通过浏览器的渲染器来做到的,当然我们可以展示Json数据类型
DEFAULTS = { # Base API policies 'DEFAULT_RENDERER_CLASSES': ( 'rest_framework.renderers.JSONRenderer', 'rest_framework.renderers.BrowsableAPIRenderer', ),