Django CBV与FBV原理及实例详解


Posted in Python onAugust 12, 2019

一、FBV

FBV(function base views) 就是在视图里使用函数处理请求。

二、CBV

CBV(class base views) 就是在视图里使用类处理请求。

Python是一个面向对象的编程语言,如果只用函数来开发,有很多面向对象的优点就错失了(继承、封装、多态)。所以Django在后来加入了Class-Based-View。可以让我们用类写View。这样做的优点主要下面两种:

提高了代码的复用性,可以使用面向对象的技术,比如Mixin(多继承)
可以用不同的函数针对不同的HTTP方法处理,而不是通过很多if判断,提高代码可读性
1、class-based views的使用

(1)写一个处理GET方法的view

用函数写的话如下所示:

from django.http import HttpResponse
def my_view(request):
   if request.method == 'GET':
      return HttpResponse('OK')

用class-based view写的话如下所示:

from django.http import HttpResponse
from django.views import View
class MyView(View):
   def get(self, request):
      return HttpResponse('OK')

(2)用url请求分配配置

Django的url是将一个请求分配给可调用的函数的,而不是一个class。针对这个问题,class-based view提供了一个as_view()静态方法(也就是类方法),调用这个方法,会创建一个类的实例,然后通过实例调用dispatch()方法,dispatch()方法会根据request的method的不同调用相应的方法来处理request(如get() , post()等)。

到这里,这些方法和function-based view差不多了,要接收request,得到一个response返回。如果方法没有定义,会抛出HttpResponseNotAllowed异常。

在url中,写法如下:

# urls.py
from django.conf.urls import url
from myapp.views import MyView
urlpatterns = [
   url(r'^index/$', MyView.as_view()),
]

类的属性可以通过两种方法设置,第一种是常见的python的方法,可以被子类覆盖:

from django.http import HttpResponse
from django.views import View
class GreetingView(View):
  name = "yuan"
  def get(self, request):
     return HttpResponse(self.name)  
# You can override that in a subclass  
class MorningGreetingView(GreetingView):
  name= "alex"

第二种方法,可以在url中指定类的属性:

在url中设置类的属性Python

urlpatterns = [
  url(r'^index/$', GreetingView.as_view(name="egon")),
]

2、使用Mixin

要理解django的class-based-view(以下简称cbv),首先要明白django引入cbv的目的是什么。在django1.3之前,generic view也就是所谓的通用视图,使用的是function-based-view(fbv),亦即基于函数的视图。有人认为fbv比cbv更pythonic,窃以为不然。python的一大重要的特性就是面向对象。

而cbv更能体现python的面向对象。cbv是通过class的方式来实现视图方法的。class相对于function,更能利用多态的特定,因此更容易从宏观层面上将项目内的比较通用的功能抽象出来。关于多态,不多解释,有兴趣的同学自己Google。总之可以理解为一个东西具有多种形态(的特性)。

cbv的实现原理通过看django的源码就很容易明白,大体就是由url路由到这个cbv之后,通过cbv内部的dispatch方法进行分发,将get请求分发给cbv.get方法处理,将post请求分发给cbv.post方法处理,其他方法类似。

怎么利用多态呢?cbv里引入了mixin的概念。Mixin就是写好了的一些基础类,然后通过不同的Mixin组合成为最终想要的类。

所以,理解cbv的基础是,理解Mixin。Django中使用Mixin来重用代码,一个View Class可以继承多个Mixin,但是只能继承一个View(包括View的子类),推荐把View写在最右边,多个Mixin写在左边。

三、CBV示例

1、CBV应用简单示例

########### urls.py
from django.contrib import admin
from django.urls import path
from app01 import views
 
urlpatterns = [
  path('admin/', admin.site.urls),
  path('login/', views.LoginView.as_view()),
] 
############views.py
from django.shortcuts import render, HttpResponse
from django.views import View
class LoginView(View):
  def get(self, request):
    return render(request, "login.html")
 
  def post(self, request):
    return HttpResponse("post...")
 
  def put(self, request):
    pass

构建login.html页面:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<form action="" method="post">
  {% csrf_token %}
  <input type="submit">
</form>
</body>
</html>

注意:

(1)CBV的本质还是一个FBV

(2)url中设置类的属性Python:

path('login/', views.LoginView.as_view()),

用户访问login,views.LoginView.as_view()一定是一个函数名,不是函数调用。

(3)页面效果

Django CBV与FBV原理及实例详解 

点击提交post请求:

Django CBV与FBV原理及实例详解

2、from django.views import View的源码查看

class View:
  """
  get:查 post:提交,添加 put:所有内容都更新  patch:只更新一部分  delete:删除
  """
  http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']

  def __init__(self, **kwargs):
    """
    Constructor. Called in the URLconf; can contain helpful extra
    keyword arguments, and other things.
    """
    # Go through keyword arguments, and either save their values to our
    # instance, or raise an error.
    for key, value in kwargs.items():
      setattr(self, key, value)

  @classonlymethod
  def as_view(cls, **initkwargs):
    """Main entry point for a request-response process."""
    for key in initkwargs:
      if key in cls.http_method_names:
        raise TypeError("You tried to pass in the %s method name as a "
                "keyword argument to %s(). Don't do that."
                % (key, cls.__name__))
      if not hasattr(cls, key):
        raise TypeError("%s() received an invalid keyword %r. as_view "
                "only accepts arguments that are already "
                "attributes of the class." % (cls.__name__, key))

    def view(request, *args, **kwargs):
      self = cls(**initkwargs)
      if hasattr(self, 'get') and not hasattr(self, 'head'):
        self.head = self.get
      self.request = request
      self.args = args
      self.kwargs = kwargs
      return self.dispatch(request, *args, **kwargs)
    view.view_class = cls
    view.view_initkwargs = initkwargs

    # take name and docstring from class
    update_wrapper(view, cls, updated=())

    # and possible attributes set by decorators
    # like csrf_exempt from dispatch
    update_wrapper(view, cls.dispatch, assigned=())
    return view

  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)

  def http_method_not_allowed(self, request, *args, **kwargs):
    logger.warning(
      'Method Not Allowed (%s): %s', request.method, request.path,
      extra={'status_code': 405, 'request': request}
    )
    return HttpResponseNotAllowed(self._allowed_methods())

  def options(self, request, *args, **kwargs):
    """Handle responding to requests for the OPTIONS HTTP verb."""
    response = HttpResponse()
    response['Allow'] = ', '.join(self._allowed_methods())
    response['Content-Length'] = '0'
    return response

  def _allowed_methods(self):
    return [m.upper() for m in self.http_method_names if hasattr(self, m)]

注意:

(1)as_view方法:

as_view是一个类方法,因此views.LoginView.as_view()需要添加(),这样才调用这个类方法。

as_view执行完,返回是view(函数名)。因此login一旦被用户访问,真正被执行是view函数。

(2)view方法:

view函数的返回值:

return self.dispatch(request, *args, **kwargs)

这里的self是谁取决于view函数是谁调用的。view——》as_view——》LoginView(View的子类)。在子类没有定义dispatch的情况下,调用父类的。

self.dispatch(request, *args, **kwargs)是执行dispatch函数。由此可见login访问,真正被执行的是dispatch方法。且返回结果是dispatch的返回结果,且一路回传到页面显示。用户看的页面是什么,完全由self.dispatch决定。

(3)dispatch方法: (分发)

request.method.lower():这次请求的请求方式小写。

self.http_method_names:['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']

判断请求方式是否在这个请求方式列表中。

handler就是反射得到的实例方法get,如果找不到则通过http_method_not_allowed返回报错。

3、自定义dispatch

from django.shortcuts import render, HttpResponse
from django.views import View
class LoginView(View):
  def dispatch(self, request, *args, **kwargs):
    print("dispath...")
    # return HttpResponse("自定义")
 
    # 两种写法
    # ret = super(LoginView, self).dispatch(request, *args, **kwargs)
    # ret = super().dispatch(request, *args, **kwargs)
    # return ret
 
  def get(self, request):
    print("get.....")
    return render(request, "login.html")
 
  def post(self, request):
    print("post....")
    return HttpResponse("post...")
 
  def put(self, request):
    pass

注意:有两种继承父类dispatch方法的方式:

ret = super(LoginView, self).dispatch(request, *args, **kwargs)
ret = super().dispatch(request, *args, **kwargs)

四、postman

谷歌的一个插件,模拟前端发get post put delete请求,下载,安装。 https://www.getpostman.com/apps

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python多线程编程(一):threading模块综述
Apr 05 Python
Python中用于计算对数的log()方法
May 15 Python
Python中二维列表如何获取子区域元素的组成
Jan 19 Python
利用Python-iGraph如何绘制贴吧/微博的好友关系图详解
Nov 02 Python
Python制作豆瓣图片的爬虫
Dec 28 Python
python实现定时提取实时日志程序
Jun 22 Python
Django 连接sql server数据库的方法
Jun 30 Python
Sanic框架基于类的视图用法示例
Jul 18 Python
Python3使用PySynth制作音乐的方法
Sep 09 Python
简单了解python列表和元组的区别
May 14 Python
python urllib和urllib3知识点总结
Feb 08 Python
Python用tkinter实现自定义记事本的方法详解
Mar 31 Python
Python利用requests模块下载图片实例代码
Aug 12 #Python
django+tornado实现实时查看远程日志的方法
Aug 12 #Python
Django结合ajax进行页面实时更新的例子
Aug 12 #Python
django fernet fields字段加密实践详解
Aug 12 #Python
利用pyecharts实现地图可视化的例子
Aug 12 #Python
django echarts饼图数据动态加载的实例
Aug 12 #Python
python scrapy爬虫代码及填坑
Aug 12 #Python
You might like
完美解决PHP中文乱码
2009/11/26 PHP
php array_walk() 数组函数
2011/07/12 PHP
PHP中extract()函数的妙用分析
2012/07/11 PHP
PHP实现数组递归转义的方法
2014/08/28 PHP
Ubuntu 16.04中Laravel5.4升级到5.6的步骤
2018/12/07 PHP
thinkPHP+LayUI 流加载实现功能
2019/09/27 PHP
很可爱的输入框
2008/08/03 Javascript
JavaScript 入门基础知识 想学习js的朋友可以参考下
2009/12/26 Javascript
JavaScript 页面编码与浏览器类型判断代码
2010/06/03 Javascript
js中hash和ico的关联分析
2015/02/05 Javascript
基于Layer+jQuery的自定义弹框
2020/05/26 Javascript
Bootstrap导航条鼠标悬停下拉菜单
2017/01/04 Javascript
简单实现js选项卡切换效果
2017/02/09 Javascript
Angular2 Service实现简单音乐播放器服务
2017/02/24 Javascript
最基础的vue.js双向绑定操作
2017/08/23 Javascript
AngularJs1.x自定义指令独立作用域的函数传入参数方法
2018/10/09 Javascript
vue中node_modules中第三方模块的修改使用详解
2019/05/31 Javascript
浅谈TypeScript 用 Webpack/ts-node 运行的配置记录
2019/10/11 Javascript
[54:28]EG vs OG 2019国际邀请赛小组赛 BO2 第一场 8.16
2019/08/18 DOTA
python中sets模块的用法实例
2014/09/30 Python
python装饰器与递归算法详解
2016/02/18 Python
查看Django和flask版本的方法
2018/05/14 Python
Python实现 PS 图像调整中的亮度调整
2019/06/28 Python
Python之time模块的时间戳,时间字符串格式化与转换方法(13位时间戳)
2019/08/12 Python
pytorch构建多模型实例
2020/01/15 Python
前后端结合实现amazeUI分页效果
2020/08/21 HTML / CSS
俄罗斯化妆品和香水网上商店:Iledebeaute
2019/01/03 全球购物
警示教育活动总结
2014/05/05 职场文书
电子银行业务授权委托书
2014/10/10 职场文书
2014年全国法制宣传日宣传活动方案
2014/11/02 职场文书
技术员岗位职责
2015/02/04 职场文书
2015年青年志愿者工作总结
2015/05/20 职场文书
python pyhs2 的安装操作
2021/04/07 Python
HTML+VUE分页实现炫酷物联网大屏功能
2021/05/27 Vue.js
如何利用React实现图片识别App
2022/02/18 Javascript
python数据处理之Pandas类型转换
2022/04/28 Python