django view视图类详解
官方文档
View官方快速上手文档:点击查看
rest_framework视图类
如何通过django原始的View方式实现视图功能,这里仅作一个演示和一个对比
from django.shortcuts import render
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 .models import UserModel
class UserListView(View):
def get(self, request):
json_list = []
user_queryset = UserModel.objects.all()[:10]
# 方法一:这里可以手动把模型数据转为json,就是很麻烦所以仅作演示
# for user in user_queryset:
# json_dict = {}
# json_dict['username'] = user.username
# json_dict['password'] = user.password
# json_dict['email'] = user.email
# json_dict['date_joined'] = user.date_joined
# json_dict['is_active'] = user.is_active
# json_dict['gender'] = user.gender
# json_dict['avatar_img'] = request.build_absolute_uri(user.avatar_img.url) if user.avatar_img else None
# json_list.append(json_dict)
# 方法二:使用django的model_to_dict方法
# for user in user_queryset:
# json_dict = model_to_dict(user)
# json_dict['avatar_img'] = request.build_absolute_uri(user.avatar_img.url) if user.avatar_img else None # 文件路径需要单独处理,否则会报错
# json_list.append(json_dict)
# 方法三:使用django提供的序列化器
json_str = serializers.serialize("json", user_queryset)
json_list = json.loads(json_str)
# 文件路径需要单独处理,否则为相对路径
for item in json_list:
item['fields']['avatar_img'] = request.build_absolute_uri(item['fields']['avatar_img']) if item['fields']['avatar_img'] else None
return JsonResponse(json_list, safe=False)
# path('users/', views.UserListView.as_view(), name='user-list'), # 列表页关于文件路径字段,一般是数据库是保存的相对路径是,而且上述的方法也不会自动完善文件路径。 虽然上述也能实现基本的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 .models import UserModel
from .serializers import UserSerializer
class UserListView(APIView):
"""
(这里的描述也很重要,在web可浏览API页面显示api的注释)
列出所有的user或者创建一个新的user。
"""
def get(self, request):
"""返回所有数据"""
user_queryset = UserModel.objects.all()
user_serializer = UserSerializer(user_queryset, many=True)
print('user_serializer.data数据类型为:',type(user_serializer.data))
return Response(user_serializer.data) # data是序列化后的数据,数据类型为QueryDict
def post(self, request):
"""创建一条新数据"""
user_serializer = UserSerializer(data=request.data) # drf的request.data会把get、POST、FILES等等的所有数据都取出来放到data里
if user_serializer.is_valid(): # 验证数据是否合法
user_serializer.save() # 这里其实是调用了里面的create()来保存到数据库
return Response(user_serializer.data, status=status.HTTP_201_CREATED)
return Response(user_serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class UserDetailView(APIView):
"""
某个用户的详细信息
"""
def get_object(self, pk):
try:
return UserModel.objects.get(pk=pk)
except UserModel.DoesNotExist:
raise Http404
def get(self, request, pk, format=None):
"""获取一条数据"""
user = self.get_object(pk)
user_serializer = UserSerializer(user)
return Response(user_serializer.data)
def put(self, request, pk):
"""更新一条数据"""
user = self.get_object(pk)
user_serializer = UserSerializer(user, data=request.data)
if user_serializer.is_valid():
user_serializer.save()
return Response(user_serializer.data)
return Response(user_serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def delete(self, request, pk, format=None):
"""删除一条数据"""
user = self.get_object(pk)
user.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
# path('users/', views.UserListView.as_view(), name='user-list'), # 列表和创建
# path('users/<int:pk>/', views.UserDetailView.as_view(), name='user-detail'), # 详情、更新和删除使用drf的serializer在解析文件路径时,会根据settings.py内的MEDIA_URL自动完善文件路径 如果View中不写某个请求的方法,代表该View不接受这种http方法 这里仅作一个演示,如果不是特别的定制,很多时候我们是用不到这个类的
GenericAPIView是直接继承APIVIew的,它额外提供了过滤器、分页等功能
from rest_framework import generics, mixins
from .models import UserModel
from .serializers import UserSerializer
class UserListView(mixins.ListModelMixin, # 仅提供list()方法混入以供get请求绑定
mixins.CreateModelMixin, # 仅提供create()方法混入以供post请求绑定
generics.GenericAPIView):
queryset = UserModel.objects.all()
serializer_class = UserSerializer
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 UserDetailView(mixins.RetrieveModelMixin, # 仅提供retrieve()方法混入以供get请求绑定
mixins.UpdateModelMixin, # 仅提供update()方法混入以供put请求绑定
mixins.DestroyModelMixin, # 仅提供destroy()方法混入以供delete请求绑定
generics.GenericAPIView):
queryset = UserModel.objects.all()
serializer_class = UserSerializer
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, permissions
from .models import UserModel
from .serializers import UserSerializer
class UserListView(generics.ListCreateAPIView):
"""
列出所有的user或者创建一个新的user。
ListCreateAPIView = GenericAPIView + ListModelMixin + CreateModelMixin
自动提供 GET(列表)和 POST(创建)方法
"""
queryset = UserModel.objects.all()
serializer_class = UserSerializer
# 视图级权限,只有经过身份验证的用户才能读写,匿名的请求获得只读访问权限
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
class UserDetailView(generics.RetrieveUpdateDestroyAPIView):
"""
某个用户的详细信息。
RetrieveUpdateDestroyAPIView = GenericAPIView + RetrieveModelMixin + UpdateModelMixin + DestroyModelMixin
自动提供 GET(详情)、PUT(更新)和 DELETE(删除)方法
"""
queryset = UserModel.objects.all()
serializer_class = UserSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)这里是直接把通用视图类和模型mixin的方法给混合好了,连http请求都自动绑定上了模型mixin的方法 其实就是继承了GenericAPIView类和模型Mixin类,然后写了个http请求方法进行绑定
GenericViewSet类仅仅是继承了GenericAPIView和ViewSetMixin。
而ViewSetMixin类重写了as_view()方法,用于http方法和action的绑定,可以注册url路由时,更加简单。
获取一个列表或者一条数据都是使用get方法,但是drf路由会判断url路径是否有参数来分配action。
ViewSet快速上手官方文档:点击查看
编辑文件 persons/views.py
from rest_framework import viewsets, mixins, permissions
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import filters
from .models import UserModel
from .serializers import UserSerializer
class UserViewSet(mixins.ListModelMixin,
mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
viewsets.GenericViewSet):
"""
用户视图集。
GenericViewSet = GenericAPIView + ViewSetMixin
配合 mixins 提供完整的 CRUD 操作:
- list: GET /users/ (列表)
- create: POST /users/ (创建)
- retrieve: GET /users/{id}/ (详情)
- update: PUT /users/{id}/ (更新)
- destroy: DELETE /users/{id}/ (删除)
"""
queryset = UserModel.objects.all()
serializer_class = UserSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
# ------如何使用过滤、搜索、排序?-------
filter_backends = (DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter) # 添加过滤器功能
filter_fields = ('gender',) # 过滤器选项配置
search_fields = ('name', 'email') # 搜索选项配置
ordering_fields = ('date_joined') # 排序选项配置
# 这里是手动绑定http请求和操作数据的模型Mixin方法(跟之前的在视图类里面的绑定有点像),然后直接放到urls.py里面的path()里面的视图参数里面
# 也可以在urls.py用路由器router对象自动绑定默认行为和注册url,这样更方便。
user_list_view = UserViewSet.as_view({
'get': 'list', # GET /users/ - 获取列表
'post': 'create', # POST /users/ - 创建新用户
})
user_detail_view = UserViewSet.as_view({
'get': 'retrieve', # GET /users/{id}/ - 获取详情
'put': 'update', # PUT /users/{id}/ - 完整更新
'patch': 'partial_update', # PATCH /users/{id}/ - 部分更新
'delete': 'destroy' # DELETE /users/{id}/ - 删除
})
# path('users/', views.user_list_view, name='user-list'), # 列表和创建
# path('users/<int:pk>/', views.user_detail_view, name='user-detail'), # 详情、更新和删除- 这是一个重要的类,很多视图操作都可以尽量用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?
编辑文件 urls.py
"""
Users app URL configuration
"""
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from . import views
app_name = 'users'
# 创建路由器并注册视图集(自动绑定 HTTP 方法和 action)
router = DefaultRouter()
router.register(r'users', views.UserViewSet, basename='user')
urlpatterns = [
# 包含路由器自动生成的 URL 路由
path('', include(router.urls)),
]from rest_framework import viewsets
from .models import UserModel
from .serializers import UserSerializer
class UserViewSet(viewsets.ModelViewSet):
"""
用户视图集。
ModelViewSet = GenericViewSet + ListModelMixin + CreateModelMixin +
RetrieveModelMixin + UpdateModelMixin + DestroyModelMixin
自动提供完整的 CRUD 操作:
- list: GET /users/ (列表)
- create: POST /users/ (创建)
- retrieve: GET /users/{id}/ (详情)
- update: PUT /users/{id}/ (完整更新)
- partial_update: PATCH /users/{id}/ (部分更新)
- destroy: DELETE /users/{id}/ (删除)
"""
queryset = UserModel.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