学习参照Github仓库的fbv文件夹
特点
无需继承类
基于函数形式的视图开发
fbv默认为get方法,如果提供post或其他类型方法,需要自行if判断
代码实现
# urls.py
urlpatterns = [
path('book/list/', views.book_list),
]
# views.py
def book_list(request):
book_name = '射雕英雄传'
return render(request, 'book_list.html', {'book_name':book_name})
客户端访问路由:http://127.0.0.1:8000/book/list/
时,触发book_list
函数
服务端执行book_list
函数的内容,处理后返回结果
特点
介绍
学习参照Github仓库的cbv_view文件夹
# urls.py
urlpatterns = [
url(r'^books/$', views.BooksView.as_view()),
url(r'^books/(?P\d+)$' , views.BookView.as_view()),
]
客户端访问特定路由,激活自定义类的as_view函数
# views.py
class BooksView(View):
def get(self, request):
pass
def post(self, request):
pass
BooksView类中没有as_view函数,从其父类View里面寻找
@classonlymethod # 允许在不实例化类的时候,调用类方法:as_view
def as_view(cls, **initkwargs):
def view(request, *args, **kwargs):
self = cls(**initkwargs)
self.setup(request, *args, **kwargs)
if not hasattr(self, "request"):
raise AttributeError(
"%s instance has no 'request' attribute. Did you override "
"setup() and forget to call super()?" % cls.__name__
)
return self.dispatch(request, *args, **kwargs)
return view
在删除了不必要的部分后,可以看出as_view函数的关键在于:其内部定义了一个view函数,并且将该函数作为返回值
即:views.BooksView.as_view() → views.BooksView.view() → views.BookView.dispatch()
self = views.BookView
def dispatch(self, request, *args, **kwargs):
# Try to dispatch to the right method; if a method doesn't exist,
# defer to the error handler. Also defer to the error handler if the
# request method isn't on the approved list.
if request.method.lower() in self.http_method_names:
handler = getattr(
self, request.method.lower(), self.http_method_not_allowed
)
else:
handler = self.http_method_not_allowed
return handler(request, *args, **kwargs)
# 补充
http_method_names = [
"get",
"post",
"put",
"patch",
"delete",
"head",
"options",
"trace",
]
可以看到dispatch函数通过getattr()方法,将"get"、"post"等请求与函数get()、post()联系在一起
- request.method.lower() = “get”
- handler = self.get() = views.BookView.get()
注意点
BookView
中重写dispatch
函数。但要注意,重写了dispatch
函数需要给定返回结果# urls.py
urlpatterns = [
url(r'^books/$', views.BooksView.as_view(para='xxx')),
]
# views.py
class BooksView(View):
para = None
def get(self, request):
pass
def post(self, request):
pass
可以在URL配置中传递额外的参数给视图类,并在视图类中使用这些参数进行处理。请注意,参数名称需要与视图类的构造函数参数名称相匹配。
as_view传入的参数必须在BooksView中定义好,不可出现未定义的类属性变量
学习参照Github仓库的cbv_apiview文件夹
同于View,但是在内容上,处理流程更加复杂
# 数据处理上的区别
# view
def post(self, request):
# ===== 差异所在 =====
data = request.body.decode()
data_dict = json.loads(data)
print(type(request.body), type(request.body.decode()), type(data_dict))
# 验证数据
btitle = data_dict.get('btitle')
bpub_date = data_dict.get('bpub_date')
if btitle is None or bpub_date is None:
return JsonResponse({'errors': '错误信息'}, status=400)
# 保存数据
book = BookInfo.objects.create(btitle=btitle, bpub_date=bpub_date)
# APIView
def post(self, request):
# ===== 差异所在 =====
ser = BookInfo_Serializers(data=request.data)
resp = {}
if ser.is_valid():
ser.save()
resp['code'] = 201
resp['code'] = ser.data
else:
print(ser.errors)
resp['code'] = 401
resp['code'] = ser.errors
return Response(resp)
支持序列化器操作,在Request和Response上都有区别
APIView继承自View
APIView | View |
---|---|
传入视图函数中的是Rest Framework的Request对象【优点-1】 | 是Django的原生HttpRequest对象【不足-1】 |
视图方法返回Rest Framework的Response对象【优点-2】 | 是Django的原生JsonResponse对象 |
【优点-1】
【不足-1】
【优点-2】
学习参照Github仓库的cbv_genericapiview_modelserializers文件夹
GenericAPIView继承自APIView
APIView回顾
GenericAPIView新增
class Book_Genericapiview_ModelSerializers(GenericAPIView):
queryset = BookInfo.objects.all()
serializer_class = BookSerializer
'''获取单一和更新和删除'''
def get(self, request, pk):
# # 方法1:APIView常规操作
# try:
# books = BookInfo.objects.get(id=pk)
# except:
# return Response({'errors':'错误信息, 数据不存在'}, status=400)
# 方法2:Genericapiview提供的操作
ser = self.get_serializer(instance=self.get_object(), many=False)
return Response(ser.data)
通过定义类基本属性:queryset和serializer_class,并结合GenericAPIView定义的类方法,实现序列化、单一数据查询、多数据查询等操作
等价操作
GenericAPIView | APIView |
---|---|
self.get_queryset() | BookInfo.objects.all() |
self.get_object() | BookInfo.objects.get(id=pk) |
self.get_serializer_class() | BookSerializer |
self.get_serializer(instance=self.get_queryset(), many=True) | BookSerializer(instance=self.get_queryset(), many=True) |
学习参照Github仓库的cbv_genericapiview_minin文件夹
并列为自定义视图类提供父类继承关系
GenericAPIView回顾
Mixin扩展
# viewspy
class Books_Genericapiview_Minin(ListModelMixin, CreateModelMixin, GenericAPIView):
# GenericAPIView规定要写的类属性
queryset = BookInfo.objects.all()
serializer_class = BookSerializer
'''获取所有和保存'''
def get(self, request):
return self.list(request)
def post(self, request):
return self.create(request)
# 找寻方法也是从左到右,继承顺序很关键
class Book_Genericapiview_Minin(RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin, GenericAPIView):
queryset = BookInfo.objects.all()
serializer_class = BookSerializer
# 自定义destory函数,改变删除逻辑
def destroy(self, request, pk):
books = self.get_object()
books.is_delete = True
books.save()
return Response({})
'''获取单一和更新和删除'''
def get(self, request, pk):
return self.retrieve(request, pk)
def put(self, request, pk):
return self.update(request, pk)
# Minin里面定义的delete是物理删除
def delete(self, request, pk):
return self.destroy(request, pk)
学习参照Github仓库的cbv_genericapiview_minin_repackaging文件夹
Mixin扩展回顾
Mixin_Repackaging扩展
# views.py
class Books_Genericapiview_Minin_Repackaging(ListCreateAPIView):
# GenericAPIView规定要写的类属性
queryset = BookInfo.objects.all()
serializer_class = BookSerializer
class Book_Genericapiview_Minin_Repackaging(RetrieveUpdateDestroyAPIView):
queryset = BookInfo.objects.all()
serializer_class = BookSerializer
# 自定义destory函数,改变删除逻辑
def destroy(self, request, pk):
books = self.get_object()
books.is_delete = True
books.save()
return Response({})
学习参照Github仓库的cbv_viewset文件夹
despatch
函数,平且可以通过as_view()
函数传参,来将get、post、put等请求,绑定到具体的函数名称上class ViewSet(ViewSetMixin, views.APIView):
"""
The base ViewSet class does not provide any actions by default.
"""
pass
Viewset继承自ViewSetMixin, views.APIView(即上文所描述的APIView)
class ViewSetMixin:
"""
This is the magic.
Overrides `.as_view()` so that it takes an `actions` keyword that performs
the binding of HTTP methods to actions on the Resource.
For example, to create a concrete view binding the 'GET' and 'POST' methods
to the 'list' and 'create' actions...
view = MyViewSet.as_view({'get': 'list', 'post': 'create'})
"""
@classonlymethod
def as_view(cls, actions=None, **initkwargs):
"""
Because of the way class based views create a closure around the
instantiated view, we need to totally reimplement `.as_view`,
and slightly modify the view function that is created and returned.
"""
pass
ViewSetMixin 重写了 as_view函数,并且实现了 Http请求(get、post、put等)与 函数操作(自定义list函数,create函数)的绑定
# views.py
class Books_Viewset(ViewSet):
queryset = BookInfo.objects.all()
serializer_class = BookSerializer
def get_all(self, request):
pass
def add_object(self, request):
pass
def get_object(self, request, pk):
pass
def update_object(self, request, pk):
pass
def delete_object(self, request, pk):
pass
# urls.py
urlpatterns = [
url(r'^books_viewset/$', views.Books_Viewset.as_view({
'get': 'get_all',
'post': 'add_object'
})),
# 有名分组:P
url(r'^books_viewset/(?P\d+)$' , views.Books_Viewset.as_view({
'get': 'get_object',
'put': 'update_object',
'delete': 'delete_object'
}))
]
- 将增删改查查在一个视图函数里面实现,但是在urls里面,还是需要通过定义两个url路由才行
- 但是基础的viewset不提供任何的具体操作函数,需要自己编写
学习参照Github仓库的cbv_viewset文件夹
ViewSet回顾
GenericViewSet_Mixin新增
# views.py
class Books_Viewset_Pro(GenericViewSet, ListModelMixin, CreateModelMixin, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
queryset = BookInfo.objects.all()
serializer_class = BookSerializer
# urls.py
urlpatterns = [
url(r'^books_viewset_pro/$', views.Books_Viewset_Pro.as_view({
'get': 'list',
'post': 'create'
})),
# 有名分组:P
url(r'^books_viewset_pro/(?P\d+)$' , views.Books_Viewset_Pro.as_view({
'get': 'retrieve',
'put': 'update',
'delete': 'destroy'
}))
]
- 在urls里面,进行http请求和默认资源操作函数的绑定
- 但是还有优化空间:视图函数的继承、urls路由绑定的简化
学习参照Github仓库的cbv_viewset文件夹
GenericViewSet_Mixin回顾
ModelViewSet新增
# views.py
class Books_Viewset_Pro_Max(ModelViewSet):
queryset = BookInfo.objects.all()
serializer_class = BookSerializer
# urls.py
from rest_framework import routers
router = routers.DefaultRouter()
router.register(r'books_viewset_pro_max', views.Books_Viewset_Pro_Max, basename='books_viewset_pro_max')
urlpatterns = [
url(r'^books_viewset_pro_max/$', views.Books_Viewset_Pro_Max.as_view({'get':'list', 'post':'create'})),
# 有名分组:P
url(r'^books_viewset_pro_max/(?P\d+)$' , views.Books_Viewset_Pro_Max.as_view({'get':'retrieve', 'put':'update', 'delete':'destroy'})),
]
urlpatterns += router.urls
- 视图函数处理,简洁优美;url路由绑定,同样简洁优美
- http请求方法与资源操作函数的绑定可以通过路由分配,也可以通过as_view()函数传参
注释
- get(请求全部数据)、get(请求单个数据)、post、put、delete,这5个http request请求缩写为:ggppd
- 将queryset和serializer写进类属性,简称为:写入q、s类属性
CBV模式 | 视图 | 路由 | 总结 |
---|---|---|---|
View(原生Django,非DRF) | 自定义ggppd及实现逻辑(写两个) | 调用as_view()函数匹配(不传参,写两个) | 通过despatch函数实现请求与资源操作函数的绑定 |
APIView | 自定义ggppd及实现逻辑(写两个) | 调用as_view()函数匹配(不传参,写两个) | 通过DRF封装提供的Request请求和Response响应,并结合序列化器,简化数据操作 |
GenericAPIView | 写入q、s类属性 | 调用as_view()函数匹配(不传参,写两个) | 将queryset和serializer写进类属性,并提供调用方法,避免重复使用序列化器 |
自定义ggppd及实现逻辑(写两个) | |||
GenericAPIView_Mixin | 写入q、s类属性 | 调用as_view()函数匹配(不传参,写两个) | 通过定义Mixin类,提供基本的资源操作函数:list、create、retrieve、update、destory |
自定义ggppd,实现逻辑由Mixin类提供(写两个) | |||
GenericAPIView_Mixin_Repackaging | 仅写入q、s类属性(写两个) | 调用as_view()函数匹配(不传参,写两个) | http请求和资源处理函数的绑定在类里面实现,完成进一步封装 |
Viewset | 写入q、s类属性 | 调用as_view()函数匹配(传参,写两个) | 通过as_view传参,完成请求资源与操作函数的绑定 |
自定义ggpppd及实现逻辑(写一个) | |||
GenericViewSet_Mixin | 仅写入q、s类属性(写一个) | 调用as_view()函数匹配(传参,写两个) | http请求和资源处理函数的绑定在类里面实现,完成进一步封装。且该情况下的传参,post只能对应create,除非重写 |
ModelViewSet | 仅写入q、s类属性(写一个) | 调用as_view()函数匹配(传参,写两个) | |
通过router路由分发(不传参,写一个) | 可通过自定义资源操作函数及请求方式和url,在增删改查查的基础上,继续扩展新的url |
维度 | 描述 |
---|---|
继承关系 | 无继承 |
视图 | 自定义ggppd及实现逻辑(写两个视图类) |
路由 | 调用as_view()函数匹配(不传参,写两个) |
总结 | 通过despatch函数实现请求(ggppd)与资源操作函数的绑定:post请求对应的资源操作函数就是 post(*args, **kwargs) |
维度 | 描述 |
---|---|
继承关系 | View |
视图 | 自定义ggppd及实现逻辑(写两个视图类) |
路由 | 调用as_view()函数匹配(不传参,写两个) |
总结 | 通过DRF封装提供的Request请求和Response响应,并结合序列化器,简化数据操作 |
维度 | 描述 |
---|---|
继承关系 | APIView |
视图 | 写入q、s类属性;自定义ggppd及实现逻辑(写两个视图类) |
路由 | 调用as_view()函数匹配(不传参,写两个) |
总结 | 将queryset和serializer写进类属性,并提供调用方法:self.get_object、self.get_queryset(),避免重复使用序列化器 |
维度 | 描述 |
---|---|
继承关系 | ListModelMixin, CreateModelMixin, GenericAPIView |
RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin, GenericAPIView | |
视图 | 写入q、s类属性;自定义ggppd,实现逻辑由Mixin类提供(写两个视图类) |
路由 | 调用as_view()函数匹配(不传参,写两个) |
总结 | 通过定义Mixin类,提供基本的资源操作函数:list、create、retrieve、update、destory |
维度 | 描述 |
---|---|
继承关系 | ListCreateAPIView:ListModelMixin, CreateModelMixin, GenericAPIView |
RetrieveUpdateDestroyAPIView:RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin, GenericAPIView | |
视图 | 仅写入q、s类属性(写两个视图类) |
路由 | 调用as_view()函数匹配(不传参,写两个) |
总结 | http请求和资源处理函数的绑定在类里面实现,完成进一步封装 |
维度 | 描述 |
---|---|
继承关系 | ViewSetMixin, APIView |
视图 | 写入q、s类属性;自定义ggppd及实现逻辑(写一个视图类) |
路由 | 调用as_view()函数匹配(传参,写两个) |
总结 | 通过as_view传参,完成请求资源与操作函数的绑定 |
维度 | 描述 |
---|---|
继承关系 | GenericViewSet, ListModelMixin, CreateModelMixin, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin |
视图 | 仅写入q、s类属性(写一个视图类) |
路由 | 调用as_view()函数匹配(传参,写两个) |
总结 | http请求和资源处理函数的绑定在类里面实现,完成进一步封装。且该情况下的传参,post只能对应create,除非重写 |
维度 | 描述 |
---|---|
继承关系 | ModelViewset |
视图 | 仅写入q、s类属性(写一个视图类) |
路由 | 调用as_view()函数匹配(不传参,写两个) |
调用router,进行路由分配(不传参,写一个) | |
总结 | 可通过自定义资源操作函数及请求方式和url,在增删改查查的基础上,继续扩展新的url |