django view视图类详解
官方文档
View官方快速上手文档:点击查看
rest_framework视图类
如何通过django原始的View方式实现视图功能,这里仅作一个演示和一个对比
import json
from django.http import HttpResponse, JsonResponse
from django.forms.models import model_to_dict
from django.core import serializers
from django.views.generic.base import View
from Persons.models import PersonModel
# from django.views.generic import ListView  # 使用这个ListView也可以
class PersonListView(View):
    def get(self, request):
        json_list = []
        person_queryset = PersonModel.objects.all()[:10]
        
        # 方法一:这里可以手动把模型数据转为json,就是很麻烦所以仅作演示
        # 问题:1.需手动解析字段;2.时间字段和文件路径字段不能被序列化,会报错
        # for person in person_queryset:
        #     json_dict = {}
        #     json_dict["name"] = person.name
        #     json_dict["sex"] = person.sex
        #     ...
        #     json_list.append(json_dict)
        
        # 方法二:这里是模型数据自动转json
        # 问题:与上面一样,时间字段和文件路径字段不能被序列化,会报错
        # for person in person_queryset:
        #     json_dict = model_to_dict(person)
        #     json_list.append(json_dict)
        
        # 方法三:使用django提供的序列化器
        # 优点:可以转换时间字段和文件字段,不会出错
        # 问题:返回的json格式不属于restful api规范
        json_str = serializers.serialize('json', person_queryset) 
        json_list = json.loads(json_str)
        
        # return HttpResponse(json.dumps(json_list), content_type="appcation/json")
        return JsonResponse(json_list, safe=False)关于文件路径字段,一般是数据库是保存的相对路径是,而且上述的方法也不会自动完善文件路径。 虽然上述也能实现基本的View的功能,不过rest_framework考虑到了更多东西,比如api文档、数据检查等等
APIView是直接继承了前面的django的View
from django.http import Http404
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from persons.models import PersonModel
from persons.serializers import PersonSerializer
class PersonListView(APIView):
    """
    (这里的描述也很重要,在web可浏览API页面显示api的注释)
    列出所有的person或者创建一个新的person。
    """
    
    # get请求:返回所有数据
    def get(self, request, format=None):
        person_queryset = PersonModel.objects.all()
        serializer = PersonSerializer(person_queryset, many=True)  # 有many参数代表是一个数组里有多个数据
        return Response(serializer.data)  # data是序列化后的数据(这里的数据类型是什么?)
    # post请求: 创建一条新数据
    def post(self, request, format=None):
        serializer = PersonSerializer(data=request.data)  # drf的request.data会把get、POST、FILES等等的所有数据都取出来放到data里
        if serializer.is_valid():
            serializer.save()  # 这里其实是调用了里面的create()来保存到数据库
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
# get方法:查找某条数据
# put方法:更新某条数据
# delete方法:删除某条数据
class PersonDetailView(APIView):
    """
    某个人员详细信息
    """
    def get_object(self, pk):
        try:
            return PersonModel.objects.get(pk=pk)
        except PersonModel.DoesNotExist:
            raise Http404
    def get(self, request, pk, format=None):
        person = self.get_object(pk)
        serializer = PersonSerializer(person)
        return Response(serializer.data)
    def put(self, request, pk, format=None):
        person = self.get_object(pk)
        serializer = PersonSerializer(person, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
    def delete(self, request, pk, format=None):
        person = self.get_object(pk)
        person.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)使用drf的serializer在解析文件路径时,会根据settings.py内的MEDIA_URL自动完善文件路径 如果View中不写某个请求的方法,代表该View不接受这种http方法 这里仅作一个演示,如果不是特别的定制,很多时候我们是用不到这个类的
GenericAPIView是直接继承APIVIew的,它额外提供了过滤器、分页等功能
from persons.models import PersonModel
from persons.serializers import PersonSerializer
from rest_framework import mixins
from rest_framework import generics
class PersonListView(mixins.ListModelMixin,  # 仅提供list()方法混入以供get请求绑定
                  mixins.CreateModelMixin,  # 仅提供create()方法混入以供post请求绑定
                  generics.GenericAPIView):
    queryset = PersonModel.objects.all()
    serializer_class = PersonSerializer
    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 PersonDetailView(mixins.RetrieveModelMixin,  # 提供retrieve()方法供get请求绑定
                    mixins.UpdateModelMixin,  # 提供update()方法供put请求绑定
                    mixins.DestroyModelMixin,  # 提供destroy()方法供delete请求绑定
                    generics.GenericAPIView):
    queryset = PersonModel.objects.all()
    serializer_class = PersonSerializer
    def get(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)
    def put(self, request, *args, **kwargs):
        return self.update(request, *args, **kwargs)
    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)这里模型mixin类,仅仅提供的操作模型数据的默认行为的方法,还需要手动把请求和这些操作模型数据的方法进行绑定
from rest_framework import generics
from rest_framework import permissions
from django.contrib.auth.models import User
from persons.serializers import UserSerializer
from persons.models import PersonModel
from persons.serializers import PersonSerializer
from persons.permissions import IsOwnerOrReadOnly
class PersonListView(generics.ListCreateAPIView):
    queryset = PersonModel.objects.all()
    serializer_class = PersonSerializer
    permission_classes = (permissions.IsAuthenticatedOrReadOnly,)  # 视图级权限,只有经过身份验证的用户才能读写,匿名的请求获得只读访问权限
    # 用户信息是作为传入请求的属性,是通过request.user获得的,需要手动关联该数据的所有者,重写方法
    # 序列化器的create()方法现在将被传递一个附加的'owner'字段以及来自请求的验证数据
    def perform_create(self, serializer):
        serializer.save(owner=self.request.user)
class PersonDetailView(generics.RetrieveUpdateDestroyAPIView):
    queryset = PersonModel.objects.all()
    serializer_class = PersonSerializer
    permission_classes = (permissions.IsAuthenticatedOrReadOnly, IsOwnerOrReadOnly,)  # 视图级权限,只有经过身份验证的用户才能读写,匿名的请求获得只读访问权限
class UserListView(generics.ListAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer
class UserDetailView(generics.RetrieveAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer这里是直接把通用视图类和模型mixin的方法给混合好了,连http请求都自动绑定上了模型mixin的方法 其实就是继承了GenericAPIView类和模型Mixin类,然后写了个http请求方法进行绑定
GenericViewSet类仅仅是继承了GenericAPIView和ViewSetMixin。
而ViewSetMixin类重写了as_view()方法,可以注册url路由时,更加简单。
ViewSet快速上手官方文档:点击查看
编辑文件 persons/views.py
from rest_framework import generics
from django.contrib.auth.models import User
from rest_framework import permissions
from rest_framework import viewsets
from rest_framework.decorators import action
from rest_framework import filters
from django_filters.rest_framework import DjangoFilterBackend
from persons.serializers import UserSerializer
from persons.models import PersonModel
from persons.serializers import PersonSerializer
from persons.permissions import IsOwnerOrReadOnly
class PersonListViewSet(viewsets.GenericViewSet, mixins.ListModelMixin):
    """
    描述
    """
    queryset = PersonModel.objects.all()  # 这里并没有真的取数据,只是生成一个sql语句
    serializer_class = PersonSerializer
    permission_classes = (permissions.IsAuthenticatedOrReadOnly, IsOwnerOrReadOnly,)
    
    # ------如何使用过滤、搜索、排序?-------
    filter_backends = (DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter)  # 添加过滤器功能
    filter_fields = ('age', 'sex')  # 过滤器选项配置
    search_fields = ('name', 'email')  # 搜索选项配置
    ordering_fields = ('age', 'create_time')  # 排序选项配置
    
    # @action(detail=True, renderer_classes=[renderers.StaticHTMLRenderer])
    def perform_create(self, serializer):
        serializer.save(owner=self.request.user)
    
    # 请注意这个函数与上面的queryset属性的功能是一样的,只是这里可以更多的定制,两者选一即可
    # def get_queryset(self):
    #    queryset = PersonModel.objects.all() 
    #    age_min = self.request.query_params.get('age_min', 0)
    #    return queryset.filter(age__gt=int(age_min))  # 自定义一个过滤选项,这里只是演示功能,不建议这么使用过滤,太麻烦
# 这里是手动绑定http请求和操作数据的模型Mixin方法(跟之前的在视图类里面的绑定有点像),然后直接放到urls.py里面的path()里面的视图参数里面
# 也可以在urls.py用路由器router对象自动绑定默认行为和注册url,这样更方便。
person_list = PersonListViewSet.as_view({
    'get': 'list',
})- 这是一个重要的类,很多视图操作都可以尽量用ViewSet来完成
viewsets.py模块里面,除了ViewSetMixin类有逻辑代码,其他类没有任何逻辑代码,仅仅是继承类来实现功能;
- ViewSetMixin类: 重写了as_view()方法用来更简单的注册URL路由,添加了action功能
- ViewSet类: 仅仅继承了APIView和ViewSetMixin,可以理解为APIView类加上了集合功能
- GenericViewSet类: 仅仅继承了GenericAPIView和ViewSetMixin,可以理解为GenericAPIView类加上了集合功能
- ReadOnlyModelViewSet类: 仅仅继承了GenericViewSet和一些查询数据的模型Mixin方法
- ModelViewSet类: 仅仅继承了GenericViewSet和所有的5个模型Mixin方法
那我们如何用路由器实现自动绑定并注册URL? 
 编辑文件 project_name/urls.py
from rest_framework.routers import DefaultRouter
from persons.views import PersonListViewSet
router = DefaultRouter()
router.register(r'persons', PersonListViewSet)
urlpatterns = [
    re_path(r'^', include(router.urls)),
    # ...
]from rest_framework import generics
from django.contrib.auth.models import User
from rest_framework import permissions
from rest_framework import viewsets
from rest_framework.decorators import action
from persons.serializers import UserSerializer
from persons.models import PersonModel
from persons.serializers import PersonSerializer
from persons.permissions import IsOwnerOrReadOnly
class PersonViewSet(viewsets.ModelViewSet):
    """
    此视图自动提供`list`,`create`,`retrieve`,`update`和`destroy`操作。
    """
    queryset = PersonModel.objects.all()
    serializer_class = PersonSerializer
    permission_classes = (permissions.IsAuthenticatedOrReadOnly, IsOwnerOrReadOnly,)
    # @action(detail=True, renderer_classes=[renderers.StaticHTMLRenderer])
    def perform_create(self, serializer):
        serializer.save(owner=self.request.user)
class UserViewSet(viewsets.ReadOnlyModelViewSet):
    """
    此视图自动提供`list`和`detail`操作。
    """
    queryset = User.objects.all()
    serializer_class = UserSerializer- 使用视图集时,可以不用自己设计URL,用router类自动处理
- 用一个视图集可替换list视图类和detail视图类的操作
自定义过滤器
drf中,过滤、搜索、排序都叫filter
from django.db.models import Q
import django_filters
from .models import PersonModel
class PersonFilter(django_filters.filters.FilterSet):
    """
    人员的过滤类
    """
    age_min = django_filters.NumberFilter(name='age', lookup_expr='gte')  # 大于等于
    age_max = django_filters.NumberFilter(name='age', lookup_expr='lte')  # 小于等于
    name = django_filters.CharFilter(name='name', lookup_expr='icontains')  # 模糊搜索,不区分大小写
    describe = django_filters.CharFilter(method='describe_method')  # 自定义函数过滤
    
    def describe_method(self, queryset, name, value):
        return queryset = queryset.filter(Q(sex="XXX")|Q(age="XXX"))
    
    
    class Meta:
        model = PersonModel
        fields = ['age_min', 'age_max', 'name']# 定制完过滤器还有在View中配置上
class PersonListViewSet(viewsets.GenericViewSet, mixins.ListModelMixin)
    # ...
    filter_class = PersonFilter过滤器官方文档:drf官方文档 django-filter文档
View继承关系
- 先尽量使用GenericViewSet、ModelViewSet、ReadOnlyModelViewSet完成功能
- 其次考虑使用XXXAPIVIew,如果需要制定那么复制这块代码后使用GenericAPIView进行定制
APIView 属性与方法
可插拔的属性
- .renderer_classes
- .parser_classes
- .authentication_classes
- .throttle_classes
- .permission_classes
- .content_negotiation_class
可插拔的方法,通常不需要重写这些方法
- .get_renderers(self)
- .get_parsers(self)
- .get_authenticators(self)
- .get_throttles(self)
- .get_permissions(self)
- .get_content_negotiator(self)
- .get_exception_handler(self)
具体的处理方法之前调用的方法
- .check_permissions(self, request)
- .check_throttles(self, request)
- .perform_content_negotiation(self, request, force=False)
视图的.dispatch()直接调用的方法,在调用其他请求方法前或后执行相关操作
- .initial(self, request, *args, **kwargs) - 在请求处理方法调用之前,用于执行权限认证和限制,并且执行内容协商。 你通常不需要重写此方法。
 
- .handle_exception(self, exc) - 在请求处理方法抛出的异常后都会被传递给这个方法,这个方法既不返回Response的实例,也不重新抛出异常。
- 默认会处理rest_framework.expceptions.APIException的任何子类异常,以及Django的Http404和PermissionDenied异常,并且返回一个适当的错误响应。
- 如果你需要在自己的API中自定义返回的错误响应,你需要重写这个方法。
 
- .initialize_request(self, request, *args, **kwargs) - 确保传递给请求处理方法的请求对象是rest Request的实例,而不是通常的Django HttpRequest的实例。
- 你通常不需要重写这个方法。
 
- .finalize_response(self, request, response, *args, **kwargs) - 确保任何从请求处理方法返回的Response对象被渲染到由内容协商决定的正确内容类型。
- 你通常不需要重写这个方法。
 
GenericAPIView 属性和方法
- queryset 和 get_queryset() - 用于从视图返回对象的查询结果集。通常你必须设置此方法或属性
 
- serializer_class 和 get_serializer_class() - 用于验证和反序列化输入以及用于序列化输出的Serializer类。 通常你必须设置此属性或方法
 
- lookup_field - 用于查找model实例的字段。默认为 'pk'。
 
- lookup_url_kwarg - 用于对象查找的URL关键字参数,默认情况下使用与 lookup_field相同的值。
 
- pagination_class - 当分页列出结果时应该使用的分页类。默认值与DEFAULT_PAGINATION_CLASS设置的值相同,即rest_framework.pagination.PageNumberPagination
 
- 当分页列出结果时应该使用的分页类。默认值与DEFAULT_PAGINATION_CLASS设置的值相同,即
- filter_backends - 过滤查询集的过滤器后端类。默认值与DEFAULT_FILTER_BACKENDS 设置的值相同。
 
- get_object(self) - 用于详细视图的对象实例查找,默认使用 lookup_field 参数过滤基本的查询集。
 
- filter_queryset(self, queryset) - 给定一个queryset,使用任何过滤器后端进行过滤,返回一个新的queryset。
 
由mixin类提供的方法:
- perform_create(self, serializer) - 在保存新对象实例时由 CreateModelMixin 调用。
 
- perform_update(self, serializer) - 在保存现有对象实例时由 UpdateModelMixin 调用。
 
- perform_destroy(self, instance) - 在删除对象实例时由 DestroyModelMixin 调用。
 
其他方法(通常不需要重写)
- get_serializer_context(self) - 返回提供给序列化的任何额外上下文的字典。默认包含requestviewformat。
 
- 返回提供给序列化的任何额外上下文的字典。默认包含
- get_serializer(self, instance=None, data=None, many=False, partial=False) - 返回一个序列化器的实例。
 
- get_paginated_response(self, data) - 返回分页样式的 Response 对象。
 
- paginate_queryset(self, queryset) - 如果需要分页查询,返回页面对象,如果没有为此视图配置分页,则返回 None。
 
- filter_queryset(self, queryset) - 给定查询集,使用任何过滤器后端进行过滤,返回一个新的查询集。
 
Mixins类
Mixin 类提供的是动作方法,而不是直接定义请求处理方法(.get() .post()等)。
 Mixin 类可以 rest_framework.mixins 导入。
- ListModelMixin - .list(request, *args, **kwargs)
 
- CreateModelMixin - .create(request, *args, **kwargs)
 
- RetrieveModelMixin - .retrieve(request, *args, **kwargs)
 
- UpdateModelMixin - .update(request, *args, **kwargs)
 
- DestroyModelMixin - .destroy(request, *args, **kwargs)
 
具体视图类
以下类是具体的通用视图类,这通常是你真正用到的,除非你需要深度定制的行为。
 这些视图类可以从 rest_framework.generics导入。
- CreateAPIView - 仅创建单个模型实例(post)
- 扩展: GenericAPIView, CreateModelMixin
 
- ListAPIView - 只读模型实例列表(get)。
- 扩展: GenericAPIView, ListModelMixin
 
- RetrieveAPIView - 只读单个模型实例(get)。
- 扩展: GenericAPIView, RetrieveModelMixin
 
- DestroyAPIView - 删除单个模型实例(delete)。
- 扩展: GenericAPIView, DestroyModelMixin
 
- UpdateAPIView - 只更新单个模型实例(put、patch)。
- 扩展: GenericAPIView, UpdateModelMixin
 
- ListCreateAPIView - 读写端点模型实例的集合(get、post)。
- 扩展: GenericAPIView, ListModelMixin, CreateModelMixin
 
- RetrieveUpdateAPIView - 读取或更新单个模型实例(get、put、patch)。
- 扩展: GenericAPIView, RetrieveModelMixin, UpdateModelMixin
 
- RetrieveDestroyAPIView - 读取或删除单个模型实例(get、delete)。
- 扩展: GenericAPIView, RetrieveModelMixin, DestroyModelMixin
 
- RetrieveUpdateDestroyAPIView - 读写删除单个模型实例(get、put、patch、delete)。
- 扩展: GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin
 
ViewSets 视图集
示例
from django.contrib.auth.models import User
from django.shortcuts import get_object_or_404
from myapps.serializers import UserSerializer
from rest_framework import viewsets
from rest_framework.response import Response
class UserViewSet(viewsets.ModelViewSet):
    serializer_class = UserRegisterSerializer
    queryset = UserModel.objects.all()
    
    def list(self, request):
        pass
    def create(self, request):
        pass
    def retrieve(self, request, pk=None):
        pass
    def update(self, request, pk=None):
        pass
    def partial_update(self, request, pk=None):
        pass
    def destroy(self, request, pk=None):
        passdrf request介绍
- request.data - drf的request.data会把POST、FILES等等的所有数据都取出来放到data里
 
- request.query_params - get请求的url参数
 
- request.parsers - 解析数据的类型:json数据,表单数据,文件上传数据,字符串数据等等基本能解析所有的数据类型
 
- request.user - 获取当前用户
 
- request.auth - 获取用户token值
 
返回模板演示
# 解析模板内容并制作Response包
from django.template import loader, Context
from django.http import HttpResponse
def index(request):
    t = loader.get_template("oneApp/index.html")#得到模板对象
    c = Context({})#制作上下文变量
    html = t.render(c)#把模板对象渲染成文本
    return HttpResponse(html)  # 制作Response包并返回在类视图中添加装饰器
需要使用method_decorator将其转换为适用于类视图方法的装饰器。
from django.views.generic.base import View
from django.shortcuts import render
from django.utils.decorators import method_decorator
# 为全部请求方法添加装饰器
@method_decorator(my_decorator, name='dispatch')
# @method_decorator(my_decorator, name='get')  # 仅为get方法添加装饰器
class DemoView(View):
    # @method_decorator(my_decorator)  # 为特定请求方法添加装饰器的另一种写法
    def get(self, request):
        print('get方法')
        return HttpResponse('ok')
    def post(self, request):
        print('post方法')
        return HttpResponse('ok')rest View
GenericAPIView
继承了APIView,添加了过滤的功能,分页的功能,序列化的功能等 GenericAPIView可以配合使用rest Mixins.Mixins提供了增删改查的功能。然后重写各种请求方法绑定到Mixins提供的增删改查方法上。
GenericAPIView可以配合使用rest ViewSetMixin
>>> views.py
from rest_framework.response import Response
from rest_framework import mixins
from rest_framework import generics
class GoodsListView(mixins.ListModelMixin, generics.GenericAPIView):
    """
    商品列表页
    """
    queryset = Goods.objects.all()
    serializer_class = GoodsSerializer
    
    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)  # list函数从mixins里面继承的,ListAPIView
继承了相应的mixin和GenericAPIView类,添加了get方法的绑定
类似还有CreateAPIView(新建数据) RetrieveAPIView(获取某项数据详情) 等
>>> views.py
from rest_framework.response import Response
from rest_framework import mixins
from rest_framework import generics
class GoodsListView(generics.ListAPIView):
    """
    商品列表页
    """
    queryset = Goods.objects.all()
    serializer_class = GoodsSerializer使用GenericAPIView提供的分页 功能
rest源码配置文件:rest_framework/settings.py DEFAULTS 通用分页设置,设置完自动实现
>>>settings.py
REST_FRAMEWORK = {  # rest总配置项
    'PAGE_SIZE': 10,
}自定义分页:
>>>views.py
from rest_framework.pagination import PageNumberPagination
class GoodsPagination(PageNumberPagination):
    page_size = 10  # 单页条数
    page_size_query_param = "page_size"  # 前台指定条数的参数名
    page_query_param = "p"
    max_page_size = 100  # 单页最大条数
class GoodsListView(generics.ListAPIView):
    ...
    pagination_class = GoodsPagination