Chapter 3:View和URLconf
前一章,我们解释了怎样建立Django工程并且运行Django开发服务器。这一章,你将了解使用Django创建动态网页的基础。
你的第一个Django页面:Hello World
作为我们的目标,让我们创建Web网页并且输出著名的例子:“Hello world”。
如果你发布简单的“Hello world” Web网页,可以不使用Web框架,你能简单地输入“Hello World”到文本文件,保存为"hello.html",上传它到Web服务器的目录上。注意,在那个过程中,你已经指定Web页面的两个关键信息:它的内容(字符串“Hello World”)和它的URL(http://www.example.com/hello.html或者如果你将它放在子目录的话,大概是http://www.example.com/files/hello.html)。
使用Django,你指定了相同的两个字符串,但是使用了不同的方法。页面的内容是使用view函数生成,并且URL是被URLconf指定。首先,让我们写我们的“Hello world” view函数。
你的第一个view
在目录mysite中使用上一章介绍的django-admin.py startproject创建一个空文件views.py。Python模块将会包含我们这一章的view。注意view.py这个名字没有任何特别的含义——Django不关注文件会叫做什么——但是作为一种习惯把文件叫做views.py是好注意,对其它开发者阅读你的代码是有好处的。
我们的“Hello world” view 是简单的。整个函数加上导入语句,你应该将它全部输入到views.py文件:
from django.http import HttpResponse
def hello(request):
return HttpResponse("Hello world")
现在让我们逐步跟踪这段代码的每一行:
- 首先,我们导入类HttpResponse,它位于django.http模块。由于稍后我们会在代码中用到,我们需要导入这个类。
- 然后,定义函数名称叫做hello的view函数
[**]每个view函数都至少有一个参数,习惯上叫做request。这是一个对象,它包含了已经出发这个view的当前Web请求,并且它是类django.http.HttpRequest的实例。这个例子中,我们没有使用request做任何事情。
[**]注意view函数的名字没有关系;它不需要使用特定命名方法而去让Django识别它。我们这儿就叫它“hello”,因为它明显地指出了view的要点。下一节,“你的第一个URLconf”,将会阐明Django怎样找到这个函数
- 函数是简单的一行:它仅仅返回一个HttpResponse对象,该对象已经使用文本“Hello world”初始化。
主要的课程是在这儿:view仅是一个Python函数,它将HttpRequest作为第一个参数并且返回HttpResponse实例。为了Python函数能成为Django view,它必须作这两件事情。(有一些 异常,稍后我们将会讲述)。
你的第一个URLconf
在这一点,如果你再运行一次python manage.py runserver,你仍然仅仅看到“Welcome to Django”消息,没见到“Hello world” view的踪迹。那是因为我们的mysite工程不知道hello view; 我们需要显式的告诉Django,通过特别的URL激活view。(继续与先前与发布静态HTML文件进行类比)。使用Django绑定view函数到特定的URL上,使用URLconf可以做到。
URLconf就像你的Django Web站点的目录。基本上,它就是URL和view函数的映射。你怎样告诉Django,“对于这些URL,调用这段代码;并且对于那些URL,调用那段代码”。例如,“当一些人访问URL /foo/,调用foo_view函数,这个函数时在views.py Python模块”。
当你在先前章节中执行 django-admin.py startproject,脚本自动为你创建URLconf:文件urls.py。默认情况下,它看起来像一些事情:
from django.conf.urls.defaults import *
# Uncomment the next two lines to enable the admin:
# from django.contrib import admin
# admin.autodiscover()
urlpatterns = patterns('',
# Example:
# (r'^mysite/', include('mysite.foo.urls')),
# Uncomment the admin/doc line below and add 'django.contrib.admindocs'
# to INSTALLED_APPS to enable admin documentation:
# (r'^admin/doc/', include('django.contrib.admindocs.urls')),
# Uncomment the next line to enable the admin:
# (r'^admin/', include(admin.site.urls)),
)
默认的URLconf包括一些通用的内容,已经被注释掉,因此激活那些特征是非常容易的,只要在合适的行去掉注释符就行。如果我们忽视了注释代码,URLconf有些必需的元素:
from diango.conf.urls.defaults import *
urlpatterns = patterns('',
)
让我们逐步分析这段代码:
第一行从django.conf.urls.defaults模块导入所有对象,它是Django的URLconf架构
。这包括调用函数patterns。
第二行调用patterns函数并且保存结果到叫做urlpatterns的变量。函数patterns仅仅传递单一的参数——空的字符串。(这个字符串是被用来提供view函数的通用前缀,将会在第8章讲述)。
需要注意的点是变量urlpattern,Django期待在你的URLconf模块中找到它。这个变量在URL和处理URL的代码之间定义了映射。默认,正如你看到的,URLconf是空的——你的Django应用是空的。(如果你的URLconf是空的,Django假设你仅仅开始了一个新的工程,因此,仅仅会显示“Welcome to Django”)
增加URL和view到URLconf,仅仅增加Python元组映射URL pattern和view函数。示例如下:
from django.conf.urls.defaults import *
from mysite.views import hello
urlpatterns = patterns('',
('^hello/$', hello),
)
注意为了简洁我们删除了注释代码。如果你想要的话,可以保留这些行。
此处我们做了两处变化:
- 首先,我们从module导入了hello view —— mysite/views.py,在Python导入语法中翻译成mysite.views。(这假设了mysite/views.py在你的Python路径上)
- 下一步,我们给urlpatterns增加了一行代码('^hello/$',hello)。这一行被引用为URLpattern。它是Python二元组,第一个是模式匹配字符串(正则表达式),第二个元素是模式使用的view函数。
简要地说,我们仅仅告诉Django任何到URL/hello/的请求都应该被view函数处理。
注意:你的Python路径
你的Python路径是系统上的目录列表,当你使用Python import 语句,你可以看到Python在哪里。
例如,比如你的Python路径是被设置为:
['','/usr/lib/python2.4/site-package','/home/username/djcode']。
如果你执行了Python语句 from foo import bar ,Python将会在当前目录中寻找模块——foo.py。(Python路径的第一个入口是空字符串,意味着“当前目录”)。如果 文件不存在,Python将会寻找文件/usr/lib/python2.4/site-packages/foo.py。如果那个文件不存在,它将会尝试 home/usernmae/djcode/foo.py。最后,如果文件不存在,它将会引起 ImportError。
如果你对Python路径的值感兴趣,开始Python交互编译器,并且输入:
>>> import sys
>>> print sys.path
通常情况下你不需要担心Python路径的设置——Python和Django可以自动帮你完成。(设置Python路径是manage.py脚本做的事情之一)
URLpattern的语法是值得讨论的,因为它也许不是明显的。尽管我们想要匹配URL /hello/ ,它看起来与其它内容明显不同。为什么呢?
- 在检查URLpattern之前,Django从每个将来的URL去除了斜杠。这意味着我们的URLpattern并没有在 /hello/ 中包含前导/。(首先,这也许不是不会直觉感觉到,但是这个需求简化了一些事情)
- 模式包含加字符(^)和美元符($)。它们是正则表达式,表示特定的含义:加字符表示“要求模式匹配字符串的开始”,美元符表示“要求模式匹配字符串结尾”
通过例子可以更好地解释这个概念。如果我们我们使用模式'^hello/'取代(结尾处没有美元符),然后任何以 /hello/ 开始的URL都会匹配,例如 /hello/foo 以及 /hello/bar ,不仅仅是/hello/。与之相似的,如果我们不使用开头的加字符(如 'hello/$'),Django将会匹配任意以 hello/ 结尾的字符串,例如 /foo/bar/hello/ 。如果我们简单的使用 hello/ ,没有使用加字符或者美元符 ,然后任意包含 hello 的字符串都会匹配,例如 /foo/hello/bar 。因此,我们都使用加字符和美元符来保证仅仅有 /hello/ 匹配。
大部分URLpattern将会以加字符开始以美元符作结尾,它易于执行更高级的匹配。
你也许想要知道如果一些人请求的URL为 /hello(也就是说,没有斜杠结尾)将会发生什么。因为我们的URLpattern要求一个结尾性的斜杠,这样的话URL就不会匹配。然而,默认情况下,任何不匹配URLpattern且不以斜杠结尾的请求URL,会被重定向到以斜杠结尾的相同URL。(这是被APPEND_SLASH Django设置)
如果你是喜欢所有的URL都以斜杠结尾(它是Django开发者的偏爱),你需要做的是增加尾斜杠到每个URLpattern并且设置APPEND_SLASH为True。如果你更喜欢你的URL而不是尾斜杠,或者你想要决定以每个URL为基础确定它,设置APPEND_SLASH为False并且将你觉得合适的符号放到URLpattern里。
另外需要注意的事情是关于URLconf是我们传递的hello。函数view作为一个对象。这是Python的关键特征(如果你让它运行,那也是好的。开发服务器自动侦测Python代码的变化,必要时重载它,因此你不需要在变化中重启服务器。)服务器运行在地址 http://127.0.0.1:8080/,打开Web浏览器并且访问 http://127.0.0.1/hello/ 。你应该看到文本“Hello World”——你的Django view的输出。
高兴点啊!你已经完成了第一个Django Web页面。
正则表达式
正则表达式是在文本中指定模式的方法。当Django URLconfs允许任意正则表达式与URL匹配,但是你也许仅仅会在少量的标识。有一些通用的标识:
符号 |
匹配 |
. |
任意一个字符 |
\d |
任意一个数字 |
[A-Z] |
A和Z之间的一个字符 |
[a-z] |
a和z之间的一个字符 |
[A-Za-z] |
任意大小写字母 |
+ |
一个或者多个字符(如,\d+匹配一个或多个数字) |
[^/]+ |
反斜杠前一个或者多个字符 |
? |
零个或者一个字符 |
* |
零个或者多个字符 |
{1,3} |
在一个和三个之间的字符 |
关于404错误的快速提醒
这一点,我们的URLconf定义了单个URLpattern:处理请求到URL /hello/ 。 当你请求不同的URL时会发生什么?
尝试运行Django开发服务器并且访问网页 http://127.0.0.1:8000/goodbye/ 或者 http://127.0.0.1:8000/hello/subdirectory/, 甚至 http://127.0.0.1:8000/ 。你应该看到“页面没有找到”等提示信息。因为你请求的URL没有在URLconf中定义Django会显示它。
这个页面的使用超出了基本的404错误信息。它也精确的告诉你URLconf使用的内容以及URLconf中的每种模式。从那些信息看,你应该能知道为什么请求URL时抛出了404。
这是对Web开发者来说都是敏感的。如果这是被部署到网络上,你不想要将其暴露给公众。由于这个原因,如果你的Django工程是在debug模式下才会显示“页面没找到”这个信息。稍后我们会解释怎样使调试模式无效。现在仅仅需要知道当你创建Django工程时,它们都在调试模式下,并且如果工程不在调试模式下,Django会输出不同的404响应。
快速了解Site Root
在上节中有一些解释,如果你访问http://127.0.0.1:8000你将会看到404错误信息。Django没有给root site增加其它的东西;任何情况下,都没有指定特殊的案例。