django(六):view和cbv
FBV即以函数的形式实现视图函数,CBV即以类的形式实现视图函数;相比而言,CBV根据请求方式书写各自的代码逻辑,结构清晰明了,但是由于多了一层反射机制,性能要差一些;FBV执行效率要高一些,但是代码逻辑看起来要混乱一些。
一、CBV源码实现
django支持以类的形式写视图函数,它需要继承自django.views.generic.base.View。可以通过from django.views.gener
- class View:
- """
- Intentionally simple parent class for all views. Only implements
- dispatch-by-method and simple sanity checking.
- """
- # 1.允许可被重写的方法列表
- 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)
- # 2.在path路径中将views.funciton改写为views.Class.as_view(),其实是执行了这个函数;initkwaegs可以写入传递的请求方式;
"""
在views.Class.as_view()时,首先把cls,也就是自己传递给了这个函数;
定义了view函数后,给view函数设置view_class和view_initkwargs属性;
将view函数作为装饰器,装饰给这个cls,以及cls中的dispatch方法;其本质上就是将initkwarga传递进去;
真正返回的是view函数,它内部实例化了当前的cls类,可以使用cls中的方法;
整个过程相当于调用了View.view方法,只不过对这个方法做了一下包裹.
感觉像是一个人模型(Class)在空间戒指(as_view)隐藏了自身的属性(view_class和view_initkwargs)和功能(view)。
"""- @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))
# 3.path调用views.Class.as_view(),也就是执行view函数;view函数实例化了一个当前Class的对象,并把request以及参数封装传递给了Class.dispatch,然后调用Class.dispatchdef 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
- # 4.dispatch负责在FBV类中反射相应的方法
"""
它从request.method中获取请求类型(假设是GET),并进行反射,并交给我们在Class中写好的对应方法(GET)去执行
那么dispatch就相当于一个请求分发器,它在请求处理前执行
"""
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:
- # 如果请求方法在self.http_method_not_allowed,就反射相应的方法
- handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
- else:
- # 否则就调用self.http_method_not_allowed
- 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'] = ''
- return response
- def _allowed_methods(self):
- return [m.upper() for m in self.http_method_names if hasattr(self, m)]
- from functools import update_wrapper
- class Person:
- def __init__(self, **kwargs):
- pass
- @classmethod
- def as_View(cls, **initkwargs):
- def view(request, *args, **kwargs):
- self = cls(**initkwargs)
- self.request = request
- self.args = args
- self.kwargs = kwargs
- return self.dispatch(request, *args, **kwargs)
- view.view_class = cls
- view.view_initkwargs = initkwargs
- update_wrapper(view, cls, updated=())
- update_wrapper(view, cls.dispatch, assigned=())
- return view
- def dispatch(self, request, *args, **kwargs):
- print(request)
- print(*args)
- print(kwargs)
- return "HttpHandler"
- if __name__ == '__main__':
- view = Person.as_View(**{"name": "Jan", "age":100})
- print(view)
- print(view.view_class)
- print(view.view_initkwargs)
- view("request", 1234, gender="female")
- print()
- """
- <function Person at 0x10c75b840>
- <class '__main__.Person'>
- {'name': 'Jan', 'age': 100}
- request
- 1234
- {'gender': 'female'}
- """
as_view的逻辑
二、使用CBV
对比FBV:
- # views.py
from django.shortcuts import render, redirect- from django.views.generic.base import View
- # CBV写法
- class Login(View):
- def get(self, request, *args, **kwargs):
- return render(request, 'app04/login.html', {"msg": ''})
- def post(self, request, *args, **kwargs):
- user= request.POST.get("user", False)
- pwd = request.POST.get("pwd", False)
- if user == "root" and pwd == "root":
- request.session["username"] = user
- return redirect("index")
- else:
- msg = "用户名或密码错误"
- return render(request, 'app04/login.html', {"msg": ''})
- # FBV写法
- def login(request):
- msg = ""
- # print(request.environ["Set-Cookie"])
- if request.method == "POST":
- user= request.POST.get("user", False)
- pwd = request.POST.get("pwd", False)
- if user == "root" and pwd == "root":
- request.session["username"] = user
- return redirect("index")
- else:
- msg = "用户名或密码错误"
- return render(request, 'app04/login.html', {"msg": msg})
- def index(request):
- username = request.session.get("username")
- if username:
- return render(request, 'app04/index.html', {'username': username})
- return redirect('login')
- # urls.py
- from django.urls import path
- from app04 import views
- urlpatterns = [
- # path('login', views.login, name="login"),
- path('index', views.index, name="index"),
- path('login', views.Login.as_view(), name="login")
- ]
三、CBV使用
django.utils.decorators.py是django内置的装饰器包,提供了对类、方法以及中间件的装饰功能。
- "Functions that help with dynamically creating decorators for views."
- # For backwards compatibility in Django 2.0.
- from contextlib import ContextDecorator # noqa
- from functools import WRAPPER_ASSIGNMENTS, update_wrapper, wraps
- class classonlymethod(classmethod):
- def __get__(self, instance, cls=None):
- if instance is not None:
- raise AttributeError("This method is available only on the class, not on instances.")
- return super().__get__(instance, cls)
- def method_decorator(decorator, name=''):
- """
- Convert a function decorator into a method decorator
- """
- # 'obj' can be a class or a function. If 'obj' is a function at the time it
- # is passed to _dec, it will eventually be a method of the class it is
- # defined on. If 'obj' is a class, the 'name' is required to be the name
- # of the method that will be decorated.
- def _dec(obj):
- is_class = isinstance(obj, type)
- if is_class:
- if name and hasattr(obj, name):
- func = getattr(obj, name)
- if not callable(func):
- raise TypeError(
- "Cannot decorate '{0}' as it isn't a callable "
- "attribute of {1} ({2})".format(name, obj, func)
- )
- else:
- raise ValueError(
- "The keyword argument `name` must be the name of a method "
- "of the decorated class: {0}. Got '{1}' instead".format(
- obj, name,
- )
- )
- else:
- func = obj
- def decorate(function):
- """
- Apply a list/tuple of decorators if decorator is one. Decorator
- functions are applied so that the call order is the same as the
- order in which they appear in the iterable.
- """
- if hasattr(decorator, '__iter__'):
- for dec in decorator[::-1]:
- function = dec(function)
- return function
- return decorator(function)
- def _wrapper(self, *args, **kwargs):
- @decorate
- def bound_func(*args2, **kwargs2):
- return func.__get__(self, type(self))(*args2, **kwargs2)
- # bound_func has the signature that 'decorator' expects i.e. no
- # 'self' argument, but it is a closure over self so it can call
- # 'func' correctly.
- return bound_func(*args, **kwargs)
- # In case 'decorator' adds attributes to the function it decorates, we
- # want to copy those. We don't have access to bound_func in this scope,
- # but we can cheat by using it on a dummy function.
- @decorate
- def dummy(*args, **kwargs):
- pass
- update_wrapper(_wrapper, dummy)
- # Need to preserve any existing attributes of 'func', including the name.
- update_wrapper(_wrapper, func)
- if is_class:
- setattr(obj, name, _wrapper)
- return obj
- return _wrapper
- # Don't worry about making _dec look similar to a list/tuple as it's rather
- # meaningless.
- if not hasattr(decorator, '__iter__'):
- update_wrapper(_dec, decorator)
- # Change the name to aid debugging.
- if hasattr(decorator, '__name__'):
- _dec.__name__ = 'method_decorator(%s)' % decorator.__name__
- else:
- _dec.__name__ = 'method_decorator(%s)' % decorator.__class__.__name__
- return _dec
- def decorator_from_middleware_with_args(middleware_class):
- """
- Like decorator_from_middleware, but return a function
- that accepts the arguments to be passed to the middleware_class.
- Use like::
- cache_page = decorator_from_middleware_with_args(CacheMiddleware)
- # ...
- @cache_page(3600)
- def my_view(request):
- # ...
- """
- return make_middleware_decorator(middleware_class)
- def decorator_from_middleware(middleware_class):
- """
- Given a middleware class (not an instance), return a view decorator. This
- lets you use middleware functionality on a per-view basis. The middleware
- is created with no params passed.
- """
- return make_middleware_decorator(middleware_class)()
- # Unused, for backwards compatibility in Django 2.0.
- def available_attrs(fn):
- """
- Return the list of functools-wrappable attributes on a callable.
- This was required as a workaround for http://bugs.python.org/issue3445
- under Python 2.
- """
- return WRAPPER_ASSIGNMENTS
- def make_middleware_decorator(middleware_class):
- def _make_decorator(*m_args, **m_kwargs):
- middleware = middleware_class(*m_args, **m_kwargs)
- def _decorator(view_func):
- @wraps(view_func)
- def _wrapped_view(request, *args, **kwargs):
- if hasattr(middleware, 'process_request'):
- result = middleware.process_request(request)
- if result is not None:
- return result
- if hasattr(middleware, 'process_view'):
- result = middleware.process_view(request, view_func, args, kwargs)
- if result is not None:
- return result
- try:
- response = view_func(request, *args, **kwargs)
- except Exception as e:
- if hasattr(middleware, 'process_exception'):
- result = middleware.process_exception(request, e)
- if result is not None:
- return result
- raise
- if hasattr(response, 'render') and callable(response.render):
- if hasattr(middleware, 'process_template_response'):
- response = middleware.process_template_response(request, response)
- # Defer running of process_response until after the template
- # has been rendered:
- if hasattr(middleware, 'process_response'):
- def callback(response):
- return middleware.process_response(request, response)
- response.add_post_render_callback(callback)
- else:
- if hasattr(middleware, 'process_response'):
- return middleware.process_response(request, response)
- return response
- return _wrapped_view
- return _decorator
- return _make_decorator
- class classproperty:
- def __init__(self, method=None):
- self.fget = method
- def __get__(self, instance, cls=None):
- return self.fget(cls)
- def getter(self, method):
- self.fget = method
- return self
装饰器
请求处理装饰:
- from django.shortcuts import render, redirect
- from django.views.generic.base import View
- from django.utils.decorators import method_decorator
- def host(func):
- def fn(request, *args, **kwargs):
- print(request.get_host())
- return func(request, *args, **kwargs)
- return fn
- def get(func):
- def fn(request, *args, **kwargs):
- print(request.method)
- return func(request, *args, **kwargs)
- return fn
- class Login(View):
- @method_decorator(host) # 给dispatch添加装饰器,所有请求方式都执行
- def dispatch(self, request, *args, **kwargs):
- return super().dispatch(request, *args, **kwargs) # dispatch反射,一定要return
- @method_decorator(get) # 给单个请求添加装饰器
- def get(self, request, *args, **kwargs):
- return render(request, 'app04/login.html', {"msg": ''})
- def post(self, request, *args, **kwargs):
- user= request.POST.get("user", False)
- pwd = request.POST.get("pwd", False)
- if user == "root" and pwd == "root":
- request.session["username"] = user
- return redirect("index")
- else:
- msg = "用户名或密码错误"
- return render(request, 'app04/login.html', {"msg": ''})
上面可以重写,把装饰器写在类上,并指定给要装饰的函数
- from django.shortcuts import render, redirect
- from django.views.generic.base import View
- from django.utils.decorators import method_decorator
- def host(func):
- def fn(request, *args, **kwargs):
- print(request.get_host())
- return func(request, *args, **kwargs)
- return fn
- def get(func):
- def fn(request, *args, **kwargs):
- print(request.method)
- return func(request, *args, **kwargs)
- return fn
- @method_decorator(get, name="get") # 给单个请求添加装饰器
- @method_decorator(host, name="dispatch") # 给dispatch添加装饰器,所有请求方式都执行;一定要通过name指定要装饰的函数
- class Login(View):
- def dispatch(self, request, *args, **kwargs):
- return super().dispatch(request, *args, **kwargs) # dispatch反射,一定要return
- def get(self, request, *args, **kwargs):
- return render(request, 'app04/login.html', {"msg": ''})
- def post(self, request, *args, **kwargs):
- user= request.POST.get("user", False)
- pwd = request.POST.get("pwd", False)
- if user == "root" and pwd == "root":
- request.session["username"] = user
- return redirect("index")
- else:
- msg = "用户名或密码错误"
- return render(request, 'app04/login.html', {"msg": ''})
四、view类及其子类
view相关类在django.views.generic文件下。它通过__all__指明了所有可以调用的视图函数及其子类。其中的"View"位于.base.py文件里,是其它类的基类,它的相关内容已经在上文做了标注。其它子类用于辅助快速构建视图函数。
- # django.views.generic.__init__.py
- from django.views.generic.base import RedirectView, TemplateView, View
- from django.views.generic.dates import (
- ArchiveIndexView, DateDetailView, DayArchiveView, MonthArchiveView,
- TodayArchiveView, WeekArchiveView, YearArchiveView,
- )
- from django.views.generic.detail import DetailView
- from django.views.generic.edit import (
- CreateView, DeleteView, FormView, UpdateView,
- )
- from django.views.generic.list import ListView
- __all__ = [
- 'View', 'TemplateView', 'RedirectView', 'ArchiveIndexView',
- 'YearArchiveView', 'MonthArchiveView', 'WeekArchiveView', 'DayArchiveView',
- 'TodayArchiveView', 'DateDetailView', 'DetailView', 'FormView',
- 'CreateView', 'UpdateView', 'DeleteView', 'ListView', 'GenericViewError',
- ]
- class GenericViewError(Exception):
- """A problem in a generic view."""
- pass
1.TemplateView
用于在get请求时直接返回一个静态模板。源码如下:
- class TemplateView(TemplateResponseMixin, ContextMixin, View): # 只支持get
- """
- Render a template. Pass keyword arguments from the URLconf to the context.
- """
- def get(self, request, *args, **kwargs):
- context = self.get_context_data(**kwargs)
- return self.render_to_response(context)
用例:
- # app01/urls.py
- from django.urls import path
- from django.views.generic import TemplateView
- urlpatterns = [
- path("example1", TemplateView.as_view(template_name="app01/example1.html")), # 不用在app01/views.py中写视图函数
- ]
- # templates/app01/example1.html
- ...
<h1>this is a example1</h1>
...
2.ListView
给静态文件传递queryset列表。源码如下:
- class ListView(MultipleObjectTemplateResponseMixin, BaseListView): # 继承了这两个父类
- """
- Render some list of objects, set by `self.model` or `self.queryset`.
- `self.queryset` can actually be any iterable of items, not just a queryset.
- """
用例:
- # app01/urls.py
- from django.urls import path
- from .models import Book
- from django.views.generic import *
- bookinfo = {
- 'queryset': Book.objects.all(), # Book.objects.all去掉括号,会动态地进行查询
- 'template_name': "app01/example2.html"
- }
- urlpatterns = [
- path("example1", TemplateView.as_view(template_name="app01/example1.html")),
- path("example2", ListView.as_view(**bookinfo)),
- ]
- # templates/app01/example2.py
- ...
- <p>example2</p>
- {% for obj in object_list %} # 注意这里的object_list,它是默认的变量名,即对应bookinfo里的queryset.
- <p>{{ obj.id }}, {{ obj.name }}</p>
- {% endfor %}
- ...
它其实等价于:
- # app01/urls.py
- from django.urls import path
- from .views import *
- urlpatterns = [
- path("example3", BookListView.as_view(),)
- ]
- # app01/views.py
- from django.views.generic import ListView
- from .models import Book
- class BookListView(ListView):
- model = Book
- queryset = Book.objects.all()
- template_name = "app01/example2.html"
- def get_context_data(self, *, object_list=None, **kwargs):
- return super().get_context_data(**kwargs)
django(六):view和cbv的更多相关文章
- Django的View(视图)和路由系统
一.Django的View(视图) 1.介绍 一个视图函数(类),简称视图,是一个简单的Python 函数(类),它接受Web请求并且返回Web响应. 响应可以是一张网页的HTML内容,一个重定向,一 ...
- Django的View(视图层)
目录 Django的View(视图层) 一.JsonResponse 二.后端接收前端的文件 三. FBV和CBV(源码分析) 四.settings.py配置文件源码分析 五. 请求对象(HttpRe ...
- django的FBV和CBV
title: python djano CBV FBV tags: python, djano, CBV, FBV grammar_cjkRuby: true --- python django的fu ...
- Django的View(视图)
Django的View(视图) 一个视图函数(类),简称视图,是一个简单的Python 函数(类),它接受Web请求并且返回Web响应. 响应可以是一张网页的HTML内容,一个重定向,一个404错误, ...
- 通过django的rest-framework……(CBV)
为什么不使用FBV,因为CBV重用性很高 先看一个例子: from django.views.generic.base import View from django.http import Http ...
- WEB框架Django之中间件/缓存/CBV/信号
一Djano的中间件 1 中间件的概念 中间件顾名思义,是介于request与respose处理之间的一道处理过程,相对比较轻量级,并且全局上改变django的输入与输出.因为改变是全局, 所有需要谨 ...
- Python学习---django知识补充之CBV
Django知识补充之CBV Django: url --> def函数 FBV[function based view] 用函数和URL进行匹配 url --> ...
- django中介模型,CBV模型,及logging日志配制
1.中介模型 中介模型,这个是在我们创建表格时,多对多添加的时候应用到的,通过制定ManyToManyField字段中的through参数来定义,为两者的关系新建一个中介class 为什么会产生这个中 ...
- day 67 Django的view 与路由
一.Django中的视图 CBV和FBV 我们之前写过的都是基于函数的view,就叫FBV.还可以把view写成基于类的. url(r'^add_publisher/',views.AddPublis ...
- Django 反向解析 request CBV
正则路径中的分组 无名分组 分组的概念:就是给某一段正则表达式用小括号括起来 无名分组按位置传参数,一一对应. view中除去request,其他形参数量要与urls中分组数量一致. 无名分组就是将括 ...
随机推荐
- hdoj1045 Fire Net(二分图最大匹配)
题意:给出一个图,其中有 . 和 X 两种,. 为通路,X表示墙,在其中放炸弹,然后炸弹不能穿过墙,问你最多在图中可以放多少个炸弹? 这个题建图有点复杂orz. 建图,首先把每一行中的可以放一个炸弹的 ...
- vue学前班004(基础指令与使用技巧)
我学vue 的最终目的是为了 做apicloud 和vue 的开发 作为配合apicloud的前端框架使用 所以项目用不到的会暂时不介绍. (强烈建议 官网案例走一遍) 基础指令的学习(结合aui ...
- iOS-CocoaPods安装及使用
1. 安装 Ruby 对于iOS开发者,CocoaPods是最方便的第三方管理工具了,但是怎么安装CocoaPods呢,安装CocoaPods之前,要确保mac已经安装上Ruby,但在安装Ruby时, ...
- 【bzoj4589】Hard Nim FWT+快速幂
题目大意:给你$n$个不大于$m$的质数,求有多少种方案,使得这$n$个数的异或和为$0$.其中,$n≤10^9,m≤10^5$. 考虑正常地dp,我们用$f[i][j]$表示前$i$个数的异或和为$ ...
- 2、如何解决xamarin没有相关教程的的指导贴
本篇文章主要在于解决xamarin相关文档偏少的问题. 最终的代码并不重要.重要的还是那种处理的方式 授人以渔 群里有群友讨论说需要读取安卓的 充电电流.这样的问题实际上在原生java有一堆.但是到了 ...
- 剑指offer三十之连续子数组的最大和
一.题目 HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学.今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决.但是,如果向量 ...
- ActiveMQ学习--001--ActiveMQ和消息中间件
一.ActiveMQ简介 1,ActiveMQ是什么 ActiveMQ是Apache推出的开源的,完全支持JMS1.1和J2EE 1.4规范的JMS Provider实现的消息中间件(MOM) 2, ...
- 关于注解Annotation第一篇
注解的定义格式如下: public @interface 注解名 {定义体} 定义体就是方法的集合,每个方法实则是声明了一个配置参数.方法的名称作为配置参数的名称,方法的返回值类型就是配置参数的类型. ...
- redis-集群创建脚本
之前建好了redis集群, 但没有找到集群重启的机制, 停电2次, 重新创建太麻烦, 于是写了个脚本辅助启动 redis的创建过程可以看: http://www.cnblogs.com/wenbron ...
- 推荐的bootstrap之 formgroup表单布局样式
一直没能找到比较好的Form Group样式,直到找到如下样式 转自 https://www.cnblogs.com/jokerjason/p/5721349.html <form class= ...