我正在深入研究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()),
)
的 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))
的的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)
从某些测试请求中记录
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
毕竟,我的问题
1
根据日志,在View.init之前调用as_view
是否意味着它甚至在创建View实例之前调用了View方法?
的 2
为什么第一次调用执行后没有调用as_view()?
我还不是Python的导入,编译和内存使用方面的专家
但我觉得他们在这里扮演了一些角色
第3
在view()的定义中,以下代码片段做了什么?
self = cls(**initkwargs)
根据日志,它会触发View.init
它是否使用initkwargs创建一个新的View实例并将其分配给正在使用的实例(self)?
如果是这样,为什么需要呢?
的 4
我们如何使用initkwargs(as_view的参数)?
答案 0 :(得分:22)
这些视图的底层实现涉及一些相当高级的Python,所以如果你是一个相对初学者,如果你发现这些代码有些混乱就不足为奇了。
您应该了解的主要内容是@classmethod
装饰器对as_view()
定义的作用。这意味着此方法不是普通方法,它在类的实例上调用(并将实例作为self
参数),但是类方法,在类本身上调用< / em>(并将类作为cls
参数)。有些语言将其称为静态方法,尽管在Python中这是我们不需要进入的第三种方法。
这是因为在urlconf中定义视图的方式。您正确地放置了WelcomeView.as_view()
- 这样做的目的是在导入urlconf时调用as_view
classmethod 。
正如我们从第1点所知,cls
是视图类本身。正如一个类一样,当你调用它时,你会得到一个对象。所以,正如你所说,我们在这里做的是实例化类,然后将该实例分配给一个名为self
的变量,就像我们在该实例的方法中一样。这里的要点是,正如我上面所说,as_view
在导入时被调用,它返回一个函数 - view
- 当浏览器请求该URL时,它又被URL调度程序调用。因此,在该函数中,我们构造并调用构成基于类的视图的类的其余部分。至于为什么需要它,见下文。
__init__
方法负责将initargs
的每个成员设置为实例属性,您可以通过常用的self.whatever
语法在视图代码中访问该属性。 / p>
那么,为什么这一切都必要?
基于类的视图带来了巨大的潜在问题,即任何直接在URLconf中实例化的类(或模块级别的任何其他类)都将在整个生命周期中持续存在。 Django通常部署的方式 - 通过WSGI - 通常意味着一个进程可以持续许多请求。如果你有多个请求持续存在的东西,你可能会遇到一些非常讨厌的线程安全漏洞 - 如果你在一个请求中设置了一个实例属性,例如,它将在后续请求中可见。
因此,这段代码不仅可以确保每个请求都获得一个新实例,而且每次在视图函数中动态构造实例时,也很难打破这种请求隔离。
答案 1 :(得分:2)
1。
首先as_view()是class method。这是一个可以在类而不是类的实例上调用的方法。在这种情况下,您可以看到它是View
上的呼叫,它是一个类而不是一个实例。
2。
加载url.conf模块时调用as_view()
- 它返回函数view()
。每次请求视图时都会调用此函数 - 不需要再次调用as_view
。
3。
在view()
函数的范围内,cls
变量是View
类(例如DetailView
,ListView
或View
的任何子项正在调用该函数)。将类方法的第一个参数称为cls
是来自PEP8的编码样式规范。它类似于我们将实例方法的第一个参数称为self的方式。所以
self = cls(**initkwargs)
与
基本相同 self = View(**initkwargs)
或self = DetailView(**initkwargs)
(取决于继承此函数的类)。
正如您所说,这是实例化类的新实例。到目前为止,还没有实例化View
对象。
4。 最后,在创建类的实例时使用initkwargs。它就像添加每个键,值对作为新视图对象的属性一样简单 -
for key, value in kwargs.iteritems():
setattr(self, key, value)