CBV类视图继承
-
-
入口
-
不能使用请求方法的名字作为参数的名字
-
只能接受已经存在的属性对应的参数
-
定义了一个view
-
创建了一个类视图对象
-
保留,拷贝传递进来的属性和参数
-
调用dispatch方法
-
分发
-
如果请求方法在我们的允许的列表中
-
从自己这个对象中获取请求方法名字小写对应的属性,如果没有找到,会给一个默认http_method_not_allowded
-
-
如果请求方法不在我们允许的列表中,直接就是http_method_not_allowed
-
之后将参数传递,调用函数
-
-
-
-
默认实现了options
-
获取接口信息,可以获取接口都允许什么请求
-
- 类视图继承自View;注册时使用as_view()
1 from django.conf.urls import url 2 from CBV import views 3 4 urlpatterns = [ 5 url(r'^hello/', views.HelloCBV.as_view(msg='Sleeping'), name='hello'), 6 url(r'^books/', views.BooksCBV.as_view(), name='books'), 7 ]
- as_view 源码(base.py文件中)
- 流程:as_view --> dispatch分发 --> 调用实现请求方法对应的函数名
1 class View(object): 2 """ 3 Intentionally simple parent class for all views. Only implements 4 dispatch-by-method and simple sanity checking. 5 """ 6 # 允许的请求方法 7 http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace'] 8 9 def __init__(self, **kwargs): 10 """ 11 Constructor. Called in the URLconf; can contain helpful extra 12 keyword arguments, and other things. 13 """ 14 # Go through keyword arguments, and either save their values to our 15 # instance, or raise an error. 16 for key, value in six.iteritems(kwargs): 17 setattr(self, key, value) 18 19 @classonlymethod 20 def as_view(cls, **initkwargs): 21 """ 22 Main entry point for a request-response process. 23 """ 24 for key in initkwargs:
# 判断,不可以使用请求方法的名字作为参数名字 25 if key in cls.http_method_names: 26 raise TypeError("You tried to pass in the %s method name as a " 27 "keyword argument to %s(). Don't do that." 28 % (key, cls.__name__))
# 只能接收类视图中已经存在的属性作为参数名(路由中的变量名要和视图函数中的属性名相同) 29 if not hasattr(cls, key): 30 raise TypeError("%s() received an invalid keyword %r. as_view " 31 "only accepts arguments that are already " 32 "attributes of the class." % (cls.__name__, key)) 33 # 定义了一个view 34 def view(request, *args, **kwargs): 35 self = cls(**initkwargs) 36 if hasattr(self, 'get') and not hasattr(self, 'head'): 37 self.head = self.get # 默认get请求和head请求方式一样;支持get就支持head请求 38 self.request = request # 将请求的参数,属性记录下来(三行) 39 self.args = args 40 self.kwargs = kwargs
# 返回时调用dispatch方法 41 return self.dispatch(request, *args, **kwargs) 42 view.view_class = cls 43 view.view_initkwargs = initkwargs 44 45 # take name and docstring from class 46 update_wrapper(view, cls, updated=()) 47 48 # and possible attributes set by decorators 49 # like csrf_exempt from dispatch 50 update_wrapper(view, cls.dispatch, assigned=()) 51 return view 52
53 def dispatch(self, request, *args, **kwargs): 54 # Try to dispatch to the right method; if a method doesn't exist, 55 # defer to the error handler. Also defer to the error handler if the 56 # request method isn't on the approved list.
# 将请求方法名变为小写,并判断请求方法名是否在允许的请求列表中 57 if request.method.lower() in self.http_method_names:
# 如果在允许的请求列表中,从自己的对象中去获取请求方法名字小写的属性,没有找到给变量一个默认值 58 handler = getattr(self, request.method.lower(), self.http_method_not_allowed) 59 else: # 如果请求方法不在被允许的请求列表中,则http_method_not_allowd给变量 60 handler = self.http_method_not_allowed 61 return handler(request, *args, **kwargs) 62 63 def http_method_not_allowed(self, request, *args, **kwargs): 64 logger.warning( 65 'Method Not Allowed (%s): %s', request.method, request.path, 66 extra={'status_code': 405, 'request': request} 67 ) 68 return http.HttpResponseNotAllowed(self._allowed_methods()) 69
# 获取接口信息,获取接口都允许接收什么请求 70 def options(self, request, *args, **kwargs): 71 """ 72 Handles responding to requests for the OPTIONS HTTP verb. 73 """ 74 response = http.HttpResponse() 75 response['Allow'] = ', '.join(self._allowed_methods()) 76 response['Content-Length'] = '0' 77 return response 78 79 def _allowed_methods(self): 80 return [m.upper() for m in self.http_method_names if hasattr(self, m)]
类视图子类
[这些子类和模版是高度耦合的,在前后端分离中不会被使用。Mixin:只要继承中有此单词,就表示混合多继承]
- View:分发函数:dispatch
- ContextMixin:接收上下文(从视图函数传递到模板的内容)。函数:get_context_data
- TemplateResponseMixin:将内容渲染到模板中。属性:template_name、template_engine、response_class、content_type。函数:render_to_response
base.py 源码分析
1 # TemplateView 2 class TemplateView(TemplateResponseMixin, ContextMixin, View): 3 """ 4 A view that renders a template. This view will also pass into the context 5 any keyword arguments passed by the URLconf. 6 """ 7 def get(self, request, *args, **kwargs): 8 context = self.get_context_data(**kwargs) 9 return self.render_to_response(context)
views.py 使用
1 from django.views.generic import TemplateView 2 3 # CBV继承TemplateView,实现了get请求,继承自:TemplateResponseMixin, ContextMixin, View(实现请求分发) 5 class HelloTemplateView(TemplateView): 6 template_name = 'hello.html'
ListView是多继承子类。继承自:
-
-
TemplateResponseMixin
-
获取模板名字,首先根据template_name获取;如果没找到,自己根据应用的名字,关联模型的名字,_list.html 去查找,App/book_list.html
-
-
BaseListView
-
MultipleObjectMixin
-
ContextMixin、get_queryset、model
-
-
View:默认实现了get,渲染成了response
-
views.py 使用
1 from django.views.generic import ListView 2 from App.models import Book 3 4 # CBV继承ListView,实现了get请求。获取一个集合数据 5 # ListView继承自:MultipleObjectTemplateResponseMixin, BaseListView 6 class HelloListView(ListView): 7 template_name = 'BookList.html' 8 model = Book
book.html
1 <body> 2 <ul> 3 {% for book in object_list %} 4 <li><a href="{% url 'cbv:single' pk=book.id %}">{{ book.b_name }}</a></li> 5 {% endfor %} 7 </ul> 8 </body>
DetailView是多继承子类。继承自:
- SingleObjectTemplateResponseMixin
-
- TemplateResponseMixin:重写了获取模板名字的方法
- BaseDetailView
-
- View、SingleObjectMixin
views.py 使用
1 from django.views.generic import DetailView 2 from App.models import Book 3 4 # CBV继承DetailView。继承自SingleObjectTemplateResponseMixin, BaseDetailView 5 class HeDetailView(DetailView): 6 # template_name = 'Book.html' #. 如果不指定模版,自动去查找名字为:book_detail.html的模版 7 # model = Book 8 queryset = Book.objects.all()
urls.py
1 from django.conf.urls import url 2 from App import views 3 4 urlpatterns = [ # 单一实例:pk、slug 5 url(r'^single/(?P<pk>d+)/', views.HeDetailView.as_view(), name='single'), 6 ]
APIView源码分析
APIView源码封装的类及用途
- renderer_classes: 渲染的类
- parser_classes: 解析转换的类
- authentication_classes: 认证的类
- throttle_classes: 节流的类、控制请求频率的[此接口每分钟请求多少次]
- permission_classes: 权限的类
- content_negotiation_class:内容过滤类
- metadata_class: 元信息的类
- versioning_class: 版本控制的类
- as_view( )方法:调用父类中的as_view。对dispatch重写,主要做dispatch分发。
- 重写的dispatch方法中的 initialize_request 方法初始化一个新的request。
- 使用django的request构建了一个REST中的Request,
- 将Django中的Request作为了自己的一个私有属性 _request
- (若在rest_formwork中获取django的request,需要request._reuqest)
- initial 初始化。获取接受的渲染、获取接受的类型、版本是否支持
- perform_authentication:
- 执行用户认证。request.user[.user中将方法改为属性,并进行认证]
- 遍历用户认证器,如果认证成功会返回一个元组
- 元组中的第一个元素就是 user
- 第二个元素就是 auth [token]
- 执行用户认证。request.user[.user中将方法改为属性,并进行认证]
- check_permissions
- 检查用户权限
- 遍历权限检测器
- 只要有一个权限检测没通过就直接显示权限被拒绝
- 只有所有权限都满足,才算是拥有权限
- 检查用户权限
- check_throttles
- 检测访问频率
- 遍历频率限制器
- 如果验证不通过,就需要等待
- 检测访问频率
- perform_authentication:
- initial 初始化。获取接受的渲染、获取接受的类型、版本是否支持
- csrf_exempt
- 所有APIView的子类都是csrf豁免的
Request源码分析
-
-
它是rest_framework的request。(rest_framework.request)
-
将Django中的Request作为了自己的一个属性 _request
-
属性和方法
-
content_type: 传输内容的类型
-
stream: 流
-
query_params:查询参数。[可以在post请求中获取GET参数][将Django中的request.GET方法改名为query_params]
-
data: 处理任意数据,同时兼容 POST,PUT,PATCH
-
user: 可以直接在请求上获取用户。[相当于在请求上添加一个用户对象属性]
-
auth: 认证。相当于请求上添加了一个属性,属性值是token
-
successful_authenticator:认证成功
-
-
Response源码分析
-
-
依然是HttpResponse的子类。只要类视图继承自APIView后,
-
Response自己封装的data 直接接受字典转换成JSON [ Response(data=request.data, status(状态码)=201) ]
-
属性和方法
-
rendered_content:渲染的内容
-
-
封装 status模块中,实际上就是一个常量类
-
-
-
针对视图函数的包装
REST框架提供了两种可用于编写API视图的包装器(wrappers)
-
- CBV:APIView
- FBV:添加 @api_view装饰器;必须手动指定允许的请求方法
- @api_view(http_method_names=['GET', 'POST'])
1 from rest_framework import status 2 from rest_framework.decorators import api_view 3 from rest_framework.response import Response 4 from RestSerializers.serializers import BookSerializer 5 6 7 @api_view(http_method_names=['GET', 'POST']) 8 def books(request): 9 print(type(request)) 10 if request.method == "GET": 11 return Response(data={"msg": "get ok"}) 12 elif request.method == "POST": 13 print(request.data) 14 book_serializer = BookSerializer(data=request.data) 15 if book_serializer.is_valid(): 16 book_serializer.save() 17 return Response(data=book_serializer.data) 18 return Response(data={'msg': 'error'}, status=status.HTTP_400_BAD_REQUEST)
APIView类视图子类
GenericAPIView继承自APIView;GenericAPIView是一个基类
-
generics包中。子类:
-
GenericAPIView
-
增加的模型的获取操作
-
get_queryset:获取查询结果集
-
get_object: 获取单个对象。lookup_field 默认pk
-
get_serializer:序列化实例
-
get_serializer_class: 获取序列化类
-
get_serializer_context: 获取序列化上下文内容
-
filter_queryset: 对查询结果集过滤
-
paginator: 直接构件的分页器
-
paginate_queryset: 对查询结果集分页
-
get_paginated_response:获取分页后的结果
-
-
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
-
-
-
mixins
-
CreateModelMixin
-
create
-
perform_create
-
get_success_headers
-
-
ListModelMixin
-
list:查询结果集,添加分页,帮你序列化
-
-
RetrieveModelMixin
-
retrieve:获取单个对象并进行序列化
-
-
DestroyModelMixin
-
destroy:
-
获取单个对象
-
调用执行删除
-
返回Respon 状态码204
-
-
perform_destroy
-
默认是模型的delete
-
如果说数据的逻辑删除。重写进行保存
-
-
-
UpdateModelMixin
-
update:获取对象,合法验证;执行更新
-
perform_update
-
partial_update:差量更新,对应的就是patch
-
-
-
viewsets继承自:
-
ViewSetMixin:
-
重写as_view。添加过滤和反向解析
-
-
GenericViewSet
-
继承自GenericAPIView
-
继承自ViewSetMixin
-
-
ViewSet
-
继承自APIView
-
继承自ViewSetMixin
-
默认啥都不支持,需要自己手动实现
-
-
ReadOnlyModelViewSet
-
只读的模型的视图集合
-
继承自RetrieveModelMixin
-
继承自ListModelMixin
-
继承自GenericViewSet
-
-
ModelViewSet
-
直接封装对象的所有操作
-
继承自GenericViewSet
-
继承自CreateModelMixin
-
继承自RetrieveModelMixin
-
继承自UpdateModelMixin
-
继承自DestroyModelMixin
-
继承自ListModelMixin
-
-
封装使用1:
- user/serializers.py
1 from rest_framework import serializers 2 from user.models import User 3 # ModelSerializer没有超链接的序列化 4 class SingleUserSerializer(serializers.ModelSerializer): 5 class Meta: 6 model = User 7 fields = ['id', 'username', 'password', 'phone',]
- user/models.py
1 from django.db import models 2 3 class User(models.Model): 4 username = models.CharField(max_length=20, unique=True) 5 password = models.CharField(max_length=128) 6 phone = models.CharField(max_length=11) 7 add_time = models.DateTimeField(auto_now=True) 8 9 class Meta: 10 db_table = 'user' 11 12 def __str__(self): 13 return self.username
- user/views.py
from rest_framework.generics import ListCreateAPIView, RetrieveUpdateDestroyAPIView from user.models import User from user.serializers import SingleUserSerializer class UserResouce(ListCreateAPIView): # 操作的模型 queryset = User.objects.all() # 此模型对应的序列化类 serializer_class = SingleUserSerializer class UserResourcePro(RetrieveUpdateDestroyAPIView): queryset = User.objects.all() serializer_class = SingleUserSerializer
- user/urls.py
1 from django.urls import path 2 from user.views import UserResouce, UserResourcePro 3 4 app_name = 'user' 5 urlpatterns = [ 6 path('single', UserResouce.as_view(), name='single'), 7 path('singlepro/<int:pk>', UserResourcePro.as_view(), name='singlepro'), 8 ]
终极封装:
- user/views.py
1 from rest_framework.viewsets import ModelViewSet 2 from user.models import User 3 from user.serializers import SingleUserSerializer 4 5 class UserResourceVPro(ModelViewSet): 6 queryset = User.objects.all() 7 serializer_class = SingleUserSerializer
- user/urls.py
1 from django.urls import path, include 2 from rest_framework.routers import DefaultRouter 3 from user.views import UserResourceVPro 4 5 router = DefaultRouter() 6 router.register(r'single', UserResourceVPro) 7 # 此方式也可以获取单个对象:http://127.0.0.1:8000/user/single/2/ # 主路由中
8 urlpatterns = [ 9 path('', include(router.urls)) 10 ]
封装使用2:
- urls.py
1 from django.conf.urls import url, include 2 from django.contrib import admin 3 from App.urls import router 4 5 urlpatterns = [ 6 url(r'^admin/', admin.site.urls), 7 url(r'^app/', include('App.urls')), 8 url(r'^app/', include(router.urls)), 9 ]
- App/urls.py
1 from django.conf.urls import url 2 from rest_framework.routers import DefaultRouter 3 from App import views 4 from App.views import GameModelViewSet 5 6 urlpatterns = [ 7 url(r'^games/$', views.GamesView.as_view()), 8 url(r'^games/(?P<pk>d+)/$', views.GameView.as_view(), name='game-detail'), 9 ] 10 11 router = DefaultRouter() 12 router.register(r'progames', GameModelViewSet)
- App/models.py
1 from django.db import models 2 3 class Game(models.Model): 4 g_name = models.CharField(max_length=32) 5 g_price = models.FloatField(default=0)
- App/serializers.py
1 from rest_framework import serializers 2 from App.models import Game 3 # HyperlinkedModelSerializer带超链接的序列化 4 class GameSerializer(serializers.HyperlinkedModelSerializer): 5 6 class Meta: 7 model = Game 8 fields = ('url', 'id', 'g_name', 'g_price')
- App/views.py
1 from rest_framework.generics import ListCreateAPIView, RetrieveUpdateDestroyAPIView 2 from rest_framework.viewsets import ModelViewSet 3 from App.models import Game 4 from App.serializers import GameSerializer 5 6 7 class GamesView(ListCreateAPIView): 8 serializer_class = GameSerializer 9 queryset = Game.objects.all() 10 11 12 class GameView(RetrieveUpdateDestroyAPIView): 13 serializer_class = GameSerializer 14 queryset = Game.objects.all() 15 16 17 class GameModelViewSet(ModelViewSet): 18 serializer_class = GameSerializer 19 queryset = Game.objects.all()