13 python django django-views django-generic-views python-2.7
我正在深入研究Django的通用视图,弄清楚它们如何返回一个简单的HttpResponse对象,就像一个简单的视图函数一样.
我编写了一个简单的测试项目,并在文件django/views/generic/base.py中定义的基本View类中添加了一些日志记录命令,以便我可以跟踪引擎底下发生的事情.
我在研究过程中遇到了一些问题.
我一直在努力保持这篇文章的简短,但是,为了完全理解我认为必须包含代码片段和日志.
我会非常感谢任何花时间发表一些有用评论的人,可能会回答我的一些问题.
urls.py
from django.conf.urls import patterns, url
from views import WelcomeView
urlpatterns = patterns('',
url(r'^welcome/(?P<name>\w+)/$', WelcomeView.as_view()),
)
Run Code Online (Sandbox Code Playgroud)
views.py
from django.http import HttpResponse
from django.views.generic import View
class WelcomeView(View):
def get(self, request, name):
return HttpResponse('What is up, {0}?'.format(name))
Run Code Online (Sandbox Code Playgroud)
Django的/视图/通用/ base.py
class View(object):
"""
Intentionally simple parent class for all views. Only implements
dispatch-by-method and simple sanity checking.
"""
http_method_names = ['get', 'post', 'put', 'delete', 'head', 'options', 'trace']
def __init__(self, **kwargs):
#####logging
logging.error('*** View.__init__ is started with kwargs: {0} ***'.format(
repr(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.iteritems():
setattr(self, key, value)
#####logging
logging.error('*** View.__init__ reached its end. No return value. ***')
@classonlymethod
def as_view(cls, **initkwargs):
#####logging
logging.error('*** View.as_view is started with initkwargs: {0} ***'.format(
repr(initkwargs)))
"""
Main entry point for a request-response process.
"""
# sanitize keyword arguments
for key in initkwargs:
if key in cls.http_method_names:
raise TypeError(u"You tried to pass in the %s method name as a "
u"keyword argument to %s(). Don't do that."
% (key, cls.__name__))
if not hasattr(cls, key):
raise TypeError(u"%s() received an invalid keyword %r" % (
cls.__name__, key))
def view(request, *args, **kwargs):
#####logging
logging.error('*** View.as_view.view is called with args: {0};\
and kwargs: {1} ***'.format(
repr(args),
repr(kwargs)))
self = cls(**initkwargs)
if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head = self.get
#####logging
logging.error('*** View.as_view.view reached its end.\
Now calls dispatch() and returns the return value.')
return self.dispatch(request, *args, **kwargs)
# 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=())
#####logging
logging.error('*** View.as_view reached its end. Now returns view. ***')
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.
#####logging
logging.error('*** View.dispatch called, with args: {0};\
and kwargs: {1} ***'.format(
repr(args),
repr(kwargs)))
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
self.request = request
self.args = args
self.kwargs = kwargs
#####logging
logging.error('*** View.dispatch reached its end.\
Now calls handler and returns the return value. ***')
return handler(request, *args, **kwargs)
def http_method_not_allowed(self, request, *args, **kwargs):
allowed_methods = [m for m in self.http_method_names if hasattr(self, m)]
logger.warning('Method Not Allowed (%s): %s', request.method, request.path,
extra={
'status_code': 405,
'request': self.request
}
)
return http.HttpResponseNotAllowed(allowed_methods)
Run Code Online (Sandbox Code Playgroud)
记录来自某些测试请求
Django version 1.4.5, using settings 'try1.settings'
Development server is running at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
ERROR:root:*** View.as_view is started with initkwargs: {} ***
ERROR:root:*** View.as_view reached its end. Now returns view. ***
ERROR:root:*** View.as_view.view is called with args: (); and kwargs: {'name': u'Dude'} ***
ERROR:root:*** View.__init__ is started with kwargs: {} ***
ERROR:root:*** View.__init__ reached its end. No return value. ***
ERROR:root:*** View.as_view.view reached its end. Now calls dispatch() and returns the return value.
ERROR:root:*** View.dispatch called, with args: (); and kwargs: {'name': u'Dude'} ***
ERROR:root:*** View.dispatch reached its end. Now calls handler and returns the return value. ***
[24/Feb/2013 12:43:19] "GET /welcome/Dude/ HTTP/1.1" 200 17
ERROR:root:*** View.as_view.view is called with args: (); and kwargs: {'name': u'Dude'} ***
ERROR:root:*** View.__init__ is started with kwargs: {} ***
ERROR:root:*** View.__init__ reached its end. No return value. ***
ERROR:root:*** View.as_view.view reached its end. Now calls dispatch() and returns the return value.
ERROR:root:*** View.dispatch called, with args: (); and kwargs: {'name': u'Dude'} ***
ERROR:root:*** View.dispatch reached its end. Now calls handler and returns the return value. ***
[24/Feb/2013 12:43:32] "GET /welcome/Dude/ HTTP/1.1" 200 17
[24/Feb/2013 12:44:43] "GET /welcome/ HTTP/1.1" 404 1939
ERROR:root:*** View.as_view.view is called with args: (); and kwargs: {'name': u'Bro'} ***
ERROR:root:*** View.__init__ is started with kwargs: {} ***
ERROR:root:*** View.__init__ reached its end. No return value. ***
ERROR:root:*** View.as_view.view reached its end. Now calls dispatch() and returns the return value.
ERROR:root:*** View.dispatch called, with args: (); and kwargs: {'name': u'Bro'} ***
ERROR:root:*** View.dispatch reached its end. Now calls handler and returns the return value. ***
[24/Feb/2013 12:44:59] "GET /welcome/Bro/ HTTP/1.1" 200 16
Run Code Online (Sandbox Code Playgroud)
毕竟,我的问题
1.
根据日志,在View.init之前调用as_view.
这是否意味着它甚至在创建View实例之前调用了View方法?
2.
为什么第一次调用执行后没有调用as_view()?
我还不是Python的导入,编译和内存使用方面的专家,
但我觉得他们在这里扮演了一些角色.
3.
在view()的定义中,以下代码片段的作用是什么?
self = cls(**initkwargs)
Run Code Online (Sandbox Code Playgroud)
根据日志,它会触发View.init.
它是否使用initkwargs创建一个新的View实例并将其分配给正在使用的实例(self)?
如果是这样,为什么需要呢?
4.
我们如何使用initkwargs(as_view的参数)?
Dan*_*man 22
这些意见的底层实现涉及到一些相当高级的Python,所以如果你是一个初学者相对,如果你发现一些这个代码混淆的,这并不奇怪.
您应该了解的主要内容是@classmethod
装饰器对定义的影响as_view()
.这意味着这个方法不是一个普通的方法,它在类的实例上调用(并将实例作为self
参数),而是一个类方法,它在类本身上调用(并以类作为cls
参数) .有些语言将其称为静态方法,尽管在Python中这是我们不需要进入的第三种方法.
这是因为在urlconf中定义视图的方式.你正确地说WelcomeView.as_view()
- 这样做是在导入urlconf时调用as_view
classmethod .
正如我们从第1点所知,cls
是视图类本身.正如一个类一样,当你调用它时,你会得到一个对象.所以,正如你所说,我们在这里做的是实例化类,然后将该实例分配给一个被调用的变量self
,就好像我们在该实例的方法中一样.这里的要点是,如上所述,as_view
在导入时被调用,它返回一个函数 - view
当浏览器请求该URL时,由URL调度程序调用该函数.因此,在该函数中,我们构造并调用构成基于类的视图的类的其余部分.至于为什么需要它,见下文.
该__init__
方法负责将每个成员设置initargs
为实例属性,您可以通过常用self.whatever
语法在视图代码中访问它.
那么,为什么这一切都是必要的呢?
基于类的视图带来了巨大的潜在问题,即任何直接在URLconf中实例化的类(或模块级别的任何其他类)都将在整个生命周期中持续存在.Django通常部署的方式 - 通过WSGI - 通常意味着一个进程可以持续许多请求.如果你有多个请求持续存在的东西,你可能会遇到一些非常讨厌的线程安全漏洞 - 例如,如果你在一个请求中设置了一个实例属性,它将在后续请求中可见.
因此,此代码不仅确保每个请求获得一个新实例,而且每次在视图函数内动态构造实例时,也很难打破该请求隔离.
归档时间: |
|
查看次数: |
5512 次 |
最近记录: |