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结合opencv实现人脸检测与跟踪
Jun 08 Python
Python实现中文数字转换为阿拉伯数字的方法示例
May 26 Python
对python list 遍历删除的正确方法详解
Jun 29 Python
pandas每次多Sheet写入文件的方法
Dec 10 Python
在Python文件中指定Python解释器的方法
Feb 18 Python
python使用mitmproxy抓取浏览器请求的方法
Jul 02 Python
python 绘制拟合曲线并加指定点标识的实现
Jul 10 Python
Django使用中间键实现csrf认证详解
Jul 22 Python
pandas的相关系数与协方差实例
Dec 27 Python
python统计函数库scipy.stats的用法解析
Feb 25 Python
使用Python三角函数公式计算三角形的夹角案例
Apr 15 Python
python pygame 愤怒的小鸟游戏示例代码
Feb 25 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 $_SERVER当前完整url的写法
2009/11/12 PHP
PHP计算一年多少个星期和每周的开始和结束日期
2014/07/01 PHP
PHP多线程之内部多线程实例分析
2015/03/09 PHP
php实现将数组转换为XML的方法
2015/03/09 PHP
php清除和销毁session的方法分析
2015/03/19 PHP
浅谈php中的访问修饰符private、protected、public的作用范围
2016/11/20 PHP
Centos7.7 64位利用本地完整安装包安装lnmp/lamp套件教程
2021/03/09 Servers
CutePsWheel javascript libary 控制输入文本框为可使用滚轮控制的js库
2010/02/07 Javascript
给事件响应函数传参数的四种方式小结
2013/12/05 Javascript
利用JS提交表单的几种方法和验证(必看篇)
2016/09/17 Javascript
jQuery实现移动端手机商城购物车功能
2016/09/24 Javascript
JS轮播图中缓动函数的封装
2020/11/25 Javascript
Angular之指令Directive用法详解
2017/03/01 Javascript
Vue.js实现数据响应的方法
2018/08/13 Javascript
jQuery实现鼠标移到某个对象时弹出显示层功能
2018/08/23 jQuery
基于JS实现数字动态变化显示效果附源码
2019/07/18 Javascript
vue实现抖音时间转盘
2019/09/08 Javascript
微信小程序自定义波浪组件使用方法详解
2019/09/21 Javascript
Python中运行并行任务技巧
2015/02/26 Python
Python中用startswith()函数判断字符串开头的教程
2015/04/07 Python
django通过ajax发起请求返回JSON格式数据的方法
2015/06/04 Python
python编程之requests在网络请求中添加cookies参数方法详解
2017/10/25 Python
对Python3之进程池与回调函数的实例详解
2019/01/22 Python
Python实现的爬取小说爬虫功能示例
2019/03/30 Python
Django框架模型简单介绍与使用分析
2019/07/18 Python
selenium中get_cookies()和add_cookie()的用法详解
2020/01/06 Python
python分布式爬虫中消息队列知识点详解
2020/11/26 Python
在Ubuntu中安装并配置Pycharm教程的实现方法
2021/01/06 Python
css3的动画特效之动画序列(animation)
2017/12/22 HTML / CSS
canvas压缩图片以及卡片制作的方法示例
2018/12/04 HTML / CSS
html5给汉字加拼音加进度条的实现代码
2020/04/07 HTML / CSS
英国女性时尚品牌:Apricot
2018/12/04 全球购物
法国足球商店:Footcenter
2019/07/06 全球购物
党的群众路线教育实践活动领导班子对照检查材料
2014/09/25 职场文书
搞笑的婚礼主持词
2015/06/29 职场文书
教你漂亮打印Pandas DataFrames和Series
2021/05/29 Python