django项目的urls.py与views.py之间的关联
from django.conf.urls.defaults import *
from mysite.views import hello, current_datetime, hours_ahead
urlpatterns = patterns('',
(r'^hello/$', hello),
(r'^time/$', current_datetime),
(r'^time/plus/(\d{1,2})/$', hours_ahead),
)
在这个代码示例中,我们有两个文件:views.py 和 urls.py。
from django.http import Http404, HttpResponse import datetime
def hours_ahead(request, offset):
try:
offset = int(offset)
except ValueError:
raise Http404()
dt = datetime.datetime.now() + datetime.timedelta(hours=offset)
html = "<html><body>In %s hour(s), it will be %s.</body></html>" % (offset, dt)
return HttpResponse(html)
在 urls.py 文件中,提到的参数 offset 从匹配的 URL 中提取出来时,名字并不重要,位置才是关键。也就是说,offset 这个参数必须在 request 之后的位置,这样它才能正常工作。
那么,为什么会这样呢?hours_ahead 只是一个用户自己定义的方法,并不是一个类或者其他什么东西。是谁让这个参数的位置变得如此重要的呢?
所以,我想问的是,“offset = int(offset)” 这句代码有什么意义?为什么当用户设置小时数时,offset 能够从 URL 中接收到值呢?
2 个回答
Django的工作原理是这样的:它会读取这个网址模式 r'^time/plus/(\d{1,2})/$'
,然后提取出括号里面的参数(在这个例子中就是 (\d{1,2})
),接着把这个参数作为一个输入传递给 hours_ahead
函数。如果网址模式里有多个参数,它们的顺序也会决定传递给对应的视图函数时的顺序。想了解更多,可以查看这里的文档:https://docs.djangoproject.com/en/dev/topics/http/urls/
这里说到的参数 offset 是从匹配的 URL 中提取的,名字不重要,但位置很关键。它是紧跟在 request 后面的第二个参数,决定了它能做什么。可是,为什么会这样呢?
在 Python 中,你可以给一个方法传递两种类型的参数:位置参数和关键字参数。位置参数是根据方法定义中的位置来决定的,顺序很重要(所以叫位置参数,因为它们在方法定义中的位置很重要)。
位置参数必须始终有值,它们是必填的。
关键字参数可以以任何顺序传递,只要它们是在位置参数之后传递的。关键字参数可以是可选的。
下面是一个例子:
def foo(a, b, c='Hello', d='World'):
print(a,b,c,d)
a
和 b
是位置参数,是必填的。你必须给它们传值。c
和 d
是可选的关键字参数,有默认值。
第一个传入的参数会被称为 a
,第二个是 b
。之后,你可以传 d
、c
或者什么都不传:
>>> foo(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: foo() takes at least 2 arguments (1 given)
>>> foo(1,2)
(1, 2, 'Hello', 'World')
>>> foo(1,2,d='Yes')
(1, 2, 'Hello', 'Yes')
>>> foo(1,2,d='Yes',c='No')
(1, 2, 'No', 'Yes')
在 Django 的 urls.py 中,有两种方式来捕获 URL 的元素:
^time/plus/(\d+{1,2})/$
- 这个是捕获参数并作为位置参数传递。正则表达式捕获的结果会被传递给映射的请求函数的第二个参数,不管这个参数叫什么(第一个参数通常叫 request)。
为了映射上面的 URL,你必须有一个视图方法,接受 恰好两个位置参数,但它们可以随便命名。记住,request 是第一个位置参数。考虑一下这个:
def foo(request, a) # You must have two positional arguments
def foo(request, b) # It does not matter what the second argument is called
def foo(request, a, b='world') # You can have any number of
# additional keyword arguments
# but they must be optional (have defaults)
def foo(request, a, b, c='world') # This will fail because you have two
# positional arguments, but only one pattern is
# captured in the URL.
^time/plus/(?P<offset>\d+{1,2})/$
- 这种语法(叫做 命名组)将正则表达式的结果作为 关键字参数 传递给映射的 URL 函数。这意味着,两个数字的值会作为关键字参数 offset 传递给视图函数。你可以在 Django 文档 中了解更多关于命名组的信息。
如果你有上述命名组的 URL 模式,那么你的请求方法应该有以下签名:
def hours_ahead(request, offset)
作为一个例子,考虑这个 URL 模式:
^time/(\d+{1,2})/(\d+{1,2})/(?P<hello>\w+)/$
要匹配这个模式,你的视图函数必须有以下签名:
def foo(request, a, b, hello)
当你接收到以下 URL time/32/42/world/
时,a
的值会是 32,b
的值会是 42,而 hello
的值会是 world。
如果你在视图方法中把 hello
改成其他的,比如 def foo(request, a, b, blah)
,你的 URL 就无法映射,因为这个模式特别在寻找函数签名中的 关键字 hello。