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)
- 返回提供给序列化的任何额外上下文的字典。默认包含
request
view
format
。
- 返回提供给序列化的任何额外上下文的字典。默认包含
- 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):
pass
drf 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