import json
from django.http import HttpResponse, JsonResponse, HttpRequest
# Create your views here.
from django.urls import reverse
from django.views import View
from rest_framework.views import APIView
from rest_framework.generics import GenericAPIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.request import Request
from rest_framework import filters
from rest_framework.renderers import JSONRenderer, BrowsableAPIRenderer
from rest_framework.parsers import JSONParser, FormParser, MultiPartParser
from .models import Project
# from .serializers import ProjectSerializer
from . import serializers
from .filters import ProjectFilter
from utils.pagination import PageNumberPagination
# class ProjectDetailView(View):
# class ProjectDetailView(APIView):
class ProjectDetailView(GenericAPIView):
serializer_class = serializers.ProjectModelSerializer2
queryset = Project.objects.all()
def get_serializer_class(self):
if self.request.method == 'GET':
return serializers.ProjectModelSerializer1
else:
return self.serializer_class
def get(self, request, pk):
"""
数据校验过程
数据库操作过程
序列化输出过程
:param request:
:param pk:
:return:
"""
instance = self.get_object()
serializer = self.get_serializer(instance=instance)
return Response(serializer.data, status=status.HTTP_200_OK)
def put(self, request, pk):
"""
反序列化输入过程
数据校验过程
数据库操作过程
序列化输出过程
:param request:
:param pk:
:return:
"""
serializer = self.get_serializer(data=request.data, instance=self.get_object())
# if not serializer.is_valid():
# return JsonResponse(serializer.errors, status=400, json_dumps_params={'ensure_ascii': False})
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
def delete(self, request, pk):
"""
数据校验过程
数据库操作过程
:param request:
:param pk:
:return:
"""
instance = self.get_object()
instance.delete()
return Response(None, status=204)
# class ProjectView(View):
# class ProjectView(APIView):
class ProjectView(GenericAPIView):
"""
可以继承DRF中的APIView视图
1.APIView为View的子类
2.每一个实例方法的第二个参数为Request对象
3.Request在Django的HttpRequest之上做了拓展
》与HttpRequest中解析参数的方法完全兼容
》解析查询字符串参数:GET -> query_params
》解析application/x-www-form-urlencoded参数:POST -> data
》解析application/json参数:body -> data
》解析multipart/form-data参数:POST、FILES -> data
4.提供了认证、授权、限流功能
5.返回DRF中的Response
》为HttpResponse子类
》可以自动根据请求头中的Accept字段,返回相应格式的数据
》data接受序列化输出的数据(字典、嵌套字典的列表)
》status指定响应状态码
》headers修改响应头信息(字典)
6.解析器类
》提供针对请求头中Content-Type参数,自动解析请求参数
》默认的解析器有三种:JSONParser(application/json)、FormParser(application/x-www-form-urlencoded)、
MultiPartParser(multipart/form-data)
》有两种方式可以修改使用的解析器类:
方式一:全局settings.py中REST_FRAMEWORK -> DEFAULT_PARSER_CLASSES中指定需要使用的解析器类
方式二:在具体某个类视图中指定parser_classes类属性(列表),优先级高于方式一
7.渲染器类
》提供针对请求头中Accept参数,自动选择需要的渲染器,将数据以特定的格式返回
》默认的渲染器类有二种:JSONRenderer(application/json)、BrowsableAPIRenderer(text/html)
如果未指定Accept或者指定的Accept不为text/html,默认返回json数据
》有两种方式可以修改使用的解析器类:
方式一:全局settings.py中REST_FRAMEWORK -> DEFAULT_RENDERER_CLASSES中指定需要使用的渲染器类
方式二:在具体某个类视图中指定renderer_classes类属性(列表),优先级高于方式一
8.GenericAPIView类视图
》是APIView的子类,继承了APIView所有功能(认证、授权、限流、Request、Response、解析器、渲染器)
》提供了获取列表数据的相关功能(过滤、排序、分页)
》往往需要指定queryset类属性(定义当前类视图操作的查询集对象)
》往往需要指定serializer_class类属性(定义了当前类视图使用的公共序列化器类)
》使用get_queryset()方法获取queryset类属性、使用get_serializer()方法获取serializer_class类属性
》提供了get_object()方法,获取某一个模型对象
》在定义url路由时,指定接收主键值的关键字参数名称,默认为pk,如果不为pk的话,必须重写lookup_url_kwarg
》一般lookup_field类属性不需要修改(默认为pk),指定的是过滤模型对象时,使用的关键词参数名称
9.实现搜索过滤功能
》指定过滤引擎,有两种方式
方式一:在全局settings.py中指定DEFAULT_FILTER_BACKENDS(列表,指定过滤引擎的绝对路径字符串)
方式二:在类视图中filter_backends类属性(列表,指定过滤引擎的引用),优先级高于全局
》必须指定search_fields类属性,定义待校验的字段(模型类中的字段名字符串),默认忽略大小写的包含过滤
'^': 'istartswith',
'=': 'iexact',
'@': 'search',
'$': 'iregex',
》如果不指定search_fields类属性,不会进行搜索过滤
》调用视图的filter_queryset()方法,对查询集进行过滤,需要接收queryset查询集参数
》在全局settings.py中使用SEARCH_PARAM参数指定前端过滤查询参数(默认为search)
10.实现排序过滤功能
》指定过滤引擎,有两种方式
方式一:在全局settings.py中指定DEFAULT_FILTER_BACKENDS(列表,指定过滤引擎的绝对路径字符串)
方式二:在类视图中filter_backends类属性(列表,指定过滤引擎的引用),优先级高于全局
》必须指定ordering_fields类属性,定义支持排序的字段(模型类中的字段名字符串)
》如果不指定ordering_fields类属性,那么支持由 serializer_class 属性指定的序列化器上的任何可读字段进行排序
》指定ordering_fields = '__all__',指定视图应允许对模型的所有字段进行排序
》指定ordering类属性,定义默认的排序字段;也可以 Projects.objects.all().order_by('name')
》调用视图的filter_queryset()方法,对查询集进行过滤,需要接收queryset查询集参数
》在全局settings.py中使用ORDERING_PARAM参数指定前端过滤查询参数(默认为ordering)
11.实现分页功能
》指定分页引擎类,有两种方式
方式一:在全局settings.py中指定DEFAULT_PAGINATION_CLASS(指定分页引擎的绝对路径字符串)
方式二:在类视图中pagination_class类属性(指定分页引擎的引用),优先级高于全局
》必须在settings.py中DEFAULT_PAGINATION_CLASS指定PAGE_SIZE参数(指定默认每一页显示的数据条数)或者
在自定义分页引擎类指定page_size参数,如果未指定,那么分页功能不会开启
》在获取列表数据实例方法中调用paginate_queryset方法,需要接收queryset查询集对象,会返回嵌套page对象的列表
》调用get_paginated_response方法将分页数据返回,需要接收serializer.data参数
》如果需要开启前端能够指定获取每一页的数据条数,往往需要重写分页引擎类PageNumberPagination
》page_size指定默认每一页数据条数
》page_query_param设置前端指定页码的查询字符串参数名称
》page_query_description,对前端指定页码的查询字符串参数的中文描述
》必须重写page_size_query_param类属性,才能开启前端能够指定获取每一页的数据条数的功能
》page_size_query_description是page_size_query_param类属性的中文描述
》max_page_size指定前端每一页数据最大条数的最大值
》如果需要定制分页数据的返回,那么就需要重写get_paginated_response方法
需求:
1、需要实现对于项目操作的5个接口
a.创建一条项目数据
POST
b.更新一条项目数据
PUT
c.删除一条项目数据
DELETE
d.获取一条项目数据(获取详情数据)
GET
e.获取所有项目数据(获取列表数据)
GET
2.对于url与请求方法有如下要求
GET /projects/ 获取所有的项目数据(将所有的项目数据以json数组形式返回)
POST /projects/ 创建一条项目数据(前端需要以json形式传递项目参数,
创建成功之后将项目数据以json对象形式返回)
"""
# parser_classes = [FormParser, MultiPartParser]
# renderer_classes = [JSONRenderer]
serializer_class = serializers.ProjectModelSerializer2
queryset = Project.objects.all()
# queryset = Project.objects.all().order_by('-id')
# search_fields = ['=name', '^leader', '$desc']
search_fields = ['name', 'leader', 'desc', 'interfaces__name']
""" search_fields 搜索字段模糊匹配设置
# 给drf框架搜索引擎支持搜索的字段,见settings.py文件中的REST_FRAMEWORK全局设置
# http://localhost/projects/?search=100
进行模糊匹配,支持查询name字段中包含100,leader字段中包含100,desc字段中包含100,项目所关联的接口名称中包含100的内容全部搜索出来
如果需求中有了变更,需要支持其他字段的搜索,那么就在这个search_fields列表中增加该字段即可,
同理不需要搜索某字段,直接删除该字段即可,实现了配置的可插拔操作。
# interfaces__name关联字段,当前项目关联的接口名称,interfaces项目的models.py文件中Interfaces模型类中,
# projects字段设置的关联字段设置的是related_name='interfaces',所以项目表中通过interfaces__name 就能查询到所有相关的接口名称
9.实现搜索过滤功能
》指定过滤引擎,有两种方式
方式一:在全局settings.py中指定DEFAULT_FILTER_BACKENDS(列表,指定过滤引擎的绝对路径字符串)
方式二:在类视图中filter_backends类属性(列表,指定过滤引擎的引用),优先级高于全局
》必须指定search_fields类属性,定义待校验的字段(模型类中的字段名字符串),默认忽略大小写的包含过滤
'^': 'istartswith',
'=': 'iexact',
'@': 'search',
'$': 'iregex',
》如果不指定search_fields类属性,不会进行搜索过滤
》调用视图的filter_queryset()方法,对查询集进行过滤,需要接收queryset查询集参数
》在全局settings.py中使用SEARCH_PARAM参数指定前端过滤查询参数(默认为search)
"""
# 过滤搜索和过滤排序设置,filter_backends 搜索过滤设置,使用drf框架中filters的SearchFilter和OrderingFilter类进行实现
filter_backends = [filters.SearchFilter, filters.OrderingFilter]
filter_fields = ['name', 'desc', 'is_execute']
""" filter_fields特定字段精确匹配过滤设置,
# 请求示例:http://localhost/projects/?name=zhangsanfeng2222
# filter_backends 过滤搜索和过滤排序设置,使用drf框架中filters的SearchFilter和OrderingFilter类进行实现
10.实现排序过滤功能
》指定过滤引擎,有两种方式
方式一:在全局settings.py中指定DEFAULT_FILTER_BACKENDS(列表,指定过滤引擎的绝对路径字符串)
方式二:在类视图中filter_backends类属性(列表,指定过滤引擎的引用),优先级高于全局
》必须指定ordering_fields类属性,定义支持排序的字段(模型类中的字段名字符串)
》如果不指定ordering_fields类属性,那么支持由 serializer_class 属性指定的序列化器上的任何可读字段进行排序
》指定ordering_fields = '__all__',指定视图应允许对模型的所有字段进行排序
》指定ordering类属性,定义默认的排序字段;也可以 Projects.objects.all().order_by('name')
》调用视图的filter_queryset()方法,对查询集进行过滤,需要接收queryset查询集参数
》在全局settings.py中使用ORDERING_PARAM参数指定前端过滤查询参数(默认为ordering)
"""
# filterset_class = ProjectFilter
# filterset_class自定义的过滤器类,因django-filter和当前的python以及drf版本不一致,无法使用该插件,
# 虽然可以实现各种各样复杂的过滤规则,但是也不太推荐了,查询出来的结果可以让前端人员进行查询渲染
pagination_class = PageNumberPagination
""" pagination_class分页过滤设置
11.实现分页功能
》指定分页引擎类,有两种方式
方式一:在全局settings.py中指定DEFAULT_PAGINATION_CLASS(指定分页引擎的绝对路径字符串)
方式二:在类视图中pagination_class类属性(指定分页引擎的引用),优先级高于全局
》必须在settings.py中DEFAULT_PAGINATION_CLASS指定PAGE_SIZE参数(指定默认每一页显示的数据条数)或者
在自定义分页引擎类指定page_size参数,如果未指定,那么分页功能不会开启
》在获取列表数据实例方法中调用paginate_queryset方法,需要接收queryset查询集对象,会返回嵌套page对象的列表
》调用get_paginated_response方法将分页数据返回,需要接收serializer.data参数
》如果需要开启前端能够指定获取每一页的数据条数,往往需要重写分页引擎类PageNumberPagination
》page_size指定默认每一页数据条数
》page_query_param设置前端指定页码的查询字符串参数名称
》page_query_description,对前端指定页码的查询字符串参数的中文描述
》必须重写page_size_query_param类属性,才能开启前端能够指定获取每一页的数据条数的功能
》page_size_query_description是page_size_query_param类属性的中文描述
》max_page_size指定前端每一页数据最大条数的最大值
》如果需要定制分页数据的返回,那么就需要重写get_paginated_response方法
"""
# ordering_fields 排序字段设置
ordering_fields = ['name', 'id', 'leader']
def get_serializer_class(self):
if self.request.method == 'GET':
return serializers.ProjectModelSerializer1
else:
return self.serializer_class
def get(self, request: Request):
"""
数据库操作过程
序列化输出过程
需求1(搜索过滤):将项目名称、项目负责人名称、项目描述中包含指定内容的项目过滤出来
http://127.0.0.1:8000/projects/?search=100
需求2(特定过滤):将项目名称包含指定内容的项目过滤出来
http://127.0.0.1:8000/projects/?name=100
:param request:
:return:
"""
queryset = self.get_queryset()
# 需求1的实现
# search_param = request.query_params.get('search')
# instance_lst = []
# if search_param is not None:
# instance_lst.extend(list(queryset.filter(name__icontains=search_param)))
# instance_lst.extend(list(queryset.filter(leader__icontains=search_param)))
# instance_lst.extend(list(queryset.filter(desc__icontains=search_param)))
# serializer = self.get_serializer(instance=instance_lst, many=True)
queryset = self.filter_queryset(queryset)
# 需求2的实现
# name_param = request.query_params.get('name')
# if name_param is not None:
# queryset = queryset.filter(name__icontains=name_param)
# 分页操作
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(instance=page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(instance=queryset, many=True)
return Response(data=serializer.data, status=status.HTTP_200_OK)
def post(self, request: HttpRequest):
"""
反序列化输入过程
数据校验过程
数据库操作过程
序列化输出过程
request.query_params
request.data
:param request:
:return:
"""
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(data=serializer.data, status=status.HTTP_201_CREATED)