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中实现贪婪排名算法的教程
Apr 17 Python
编写Python脚本来获取Google搜索结果的示例
May 04 Python
Pycharm学习教程(4) Python解释器的相关配置
May 03 Python
python 矩阵增加一行或一列的实例
Apr 04 Python
Django Rest framework频率原理与限制
Jul 26 Python
在django模板中实现超链接配置
Aug 21 Python
python十进制转二进制的详解
Feb 07 Python
对tensorflow中cifar-10文档的Read操作详解
Feb 10 Python
关于win10在tensorflow的安装及在pycharm中运行步骤详解
Mar 16 Python
Python Selenium自动化获取页面信息的方法
Aug 31 Python
两行代码解决Jupyter Notebook中文不能显示的问题
Apr 24 Python
python index() 与 rindex() 方法的使用示例详解
Dec 24 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 批量替换html标签的实例代码
2013/11/26 PHP
Javascript Math ceil()、floor()、round()三个函数的区别
2010/03/09 Javascript
Javascript 匿名函数及其代码模式原理
2010/03/19 Javascript
jQuery实现表单input中提示文字value随鼠标焦点移进移出而显示或隐藏的代码
2010/03/21 Javascript
js输出列表实现代码
2010/09/12 Javascript
深入理解JavaScript系列(15) 函数(Functions)
2012/04/12 Javascript
JavaScript判断变量是对象还是数组的方法
2014/08/28 Javascript
JS小游戏之仙剑翻牌源码详解
2014/09/25 Javascript
javascript检测两个数组是否相似
2015/05/19 Javascript
探究JavaScript中的五种事件处理程序方式
2016/12/07 Javascript
flexslider.js实现移动端轮播
2017/02/05 Javascript
浅谈vue+webpack项目调试方法步骤
2017/09/11 Javascript
原生js实现拖拽功能基本思路详解
2018/04/18 Javascript
深入理解Promise.all
2018/08/08 Javascript
Vue中Table组件Select的勾选和取消勾选事件详解
2019/03/19 Javascript
详解JavaScript 为什么要有 Symbol 类型?
2019/04/03 Javascript
taro开发微信小程序的实践
2019/05/21 Javascript
python 中文乱码问题深入分析
2011/03/13 Python
理解python多线程(python多线程简明教程)
2014/06/09 Python
Python计算三角函数之asin()方法的使用
2015/05/15 Python
Python使用multiprocessing实现一个最简单的分布式作业调度系统
2016/03/14 Python
Python基于socket模块实现UDP通信功能示例
2018/04/10 Python
关于pytorch中网络loss传播和参数更新的理解
2019/08/20 Python
python中的global关键字的使用方法
2019/08/20 Python
python数据化运营的重要意义
2019/11/25 Python
基于python实现matlab filter函数过程详解
2020/06/08 Python
简单了解Django项目应用创建过程
2020/07/06 Python
python从PDF中提取数据的示例
2020/10/30 Python
美国排名第一的泳池用品直接来源:In The Swim
2019/09/23 全球购物
中学生学习生活的自我评价
2013/10/26 职场文书
商场端午节活动方案
2014/01/29 职场文书
交通事故调解协议书
2014/04/16 职场文书
自强之星事迹材料
2014/05/12 职场文书
中层干部竞聘演讲稿
2014/05/15 职场文书
大学生学年个人总结
2015/02/15 职场文书
毕业生登记表班级意见
2015/06/05 职场文书