Django Views和URLconf
在前一章中,我解释了如何设置一个Django项目并运行Django开发服务器。 在本章中,您将学习使用Django创建动态网页的基础知识。
你的第一个Django的Powered页面:Hello World
作为我们的第一个目标,让我们创建一个输出“hello world”事例消息的动态网页。如果您在没有web框架的情况下发布一个简单的“Hello world”网页,只需在输入“Hello world”到 文本文件,将其命名为“hello.html”,然后上传到Web服务器上的某个目录。 注意在这个过程中,您已经指定了关于该网页的两个关键信息:其内容(字符串“Hello world”)及其URL(例如http://www.example.com/hello.html)。
使用Django,你可以用不同的方式指定这两个相同的东西。 该页面的内容由一个视图函数生成,并在URLconf中指定该URL。 首先,我们来编写我们的“Hello world”视图函数。
你的第一视图
在上一章创建的内部mysite目录中,创建一个名为views.py的空文件。 这个Python模块将包含我们本章的所有views。
确保将文件放在内部mysite目录,即\mysite\mysite\目录中,而不是包含manage.py的目录。
我们的“Hello world”视图很简单。 这里是整个函数,加上import语句,你应该在views.py文件中输入:
from django.http import HttpResponse
def hello(request):
return HttpResponse("Hello world")
让我们一行一行的浏览这段代码:
- 首先,我们导入位于django.http模块中的类HttpResponse。 我们需要导入这个类,因为它稍后在我们的代码中使用。
- 接下来,我们定义一个名为hello的函数 - 视图函数。
- 每个视图函数至少需要一个参数,按约定称为请求。 这是一个包含关于触发该视图的当前Web请求的信息对象,并且是django.http.HttpRequest类的一个实例。
在这个例子中,我们没有对请求做任何事情,但它必须是视图的第一个参数。 请注意,视图函数的名称并不重要; 为了使Django能够识别它,它不必以某种方式命名。 我们在这里命名为hello world,因为这个名字清楚地表明了这个views的意义,但是它可以被命名为hello_wonderful_beautiful_world,或者是同样令人反感的东西。
下一节,“你的第一个URLconf”将阐明Django如何找到这个功能。 该函数是一个简单的单线程:它只是返回一个已经用文本“Hello world”实例化的HttpResponse对象。 这里的主要课时是:视图只是一个Python函数,它将HttpRequest作为其第一个参数,并返回一个HttpResponse的实例。 为了使Python函数成为一个Django视图,它必须完成这两件事。 (有一些例外,但我们会在稍后讨论)
你的第一个URLconf
如果在这一点上,你再次运行python manage.py runserver,你仍然会看到“欢迎使用Django”的消息,而且没有任何地方的“Hello world”视图。 那是因为我们的mysite项目还不知道hello视图; 我们需要明确地告诉Django我们在特定的URL上激活这个视图。
继续我们之前发布静态HTML文件的类比,此时我们已经创建了HTML文件,但尚未将其上传到服务器上的目录。 要使用Django将视图函数挂接到特定的URL,我们使用URLconf。
URLconf就像Django驱动的Web站点的目录。 基本上,它应该是URL和这些URL调用的视图函数之间提供映射。 这就是你如何告诉Django:“对于这个URL,调用这个代码,并为这个URL调用这个代码。”例如,当有人访问URL/foo/时,调用位于Python中的视图函数foo_view 模块views.py。
在上一章中执行django-admin startproject时,脚本会自动为您创建一个URLconf:文件urls.py。 默认情况下,它看起来像这样:
"""mysite URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/1.11/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: url(r'^$', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.conf.urls import url, include
2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls'))
"""
from django.conf.urls import url
from django.contrib import admin
urlpatterns = [
url(r'^admin/', admin.site.urls),
]
如果我们忽略文件顶部的文档注释,下面是一个URLconf的实质:
from django.conf.urls import url
from django.contrib import admin
urlpatterns = [
url(r'^admin/', admin.site.urls),
]
让我们一次一行地浏览这段代码:
- 第一行从django.conf.urls模块中导入两个函数:include允许您将完整的Python导入路径包含到另一个URLconf模块中,而url使用正则表达式模式将浏览器中的URL与模块中的URL匹配到你的Django项目中。
- 第二行从django.contrib模块调用函数admin。 这个函数被include函数调用来加载Django管理站点的URL。
- 第三行是urlpatterns - 一个简单的url()实例列表。
这里需要注意的是Django期望在URLconf模块中找到的变量urlpatterns。 这个变量定义了URL和处理这些URL的代码之间的映射。 要将URL和视图添加到URLconf,只需添加URL模式和视图函数之间的映射。 以下是如何在我们的hello视图中关联:
from django.conf.urls import url
from django.contrib import admin
from mysite.views import hello
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^hello/$', hello),
]
我们在这里做了两个改变:
- 首先,我们从模块mysite / views.py中导入hello视图,这个模块在Python导入语法中转化为mysite.views。 (这假定mysite / views.py在你的Python路径上。)
- 接下来,我们将天添加一行url(r'^ hello / $',hello)添加到urlpatterns。 这一行被称为URL模式。 url()函数告诉Django如何处理您正在配置的URL。 第一个参数是一个模式匹配的字符串(一个正则表达式;更多的在这个位上),第二个参数是该模式使用的视图函数。 url()也可以使用其他的可选参数,我们将在第7章深入讨论。
我们在这里介绍的一个更重要的细节是正则表达式字符串前面的'r'字符。 这告诉Python该字符串是一个“原始字符串” - 其内容不应该解释反斜杠。 在正常的Python字符串中,反斜杠用于转义特殊字符 - 例如字符串“\n”,它是一个包含换行符的单字符字符串。
当你添加r来使它成为一个原始字符串时,Python不会应用它的反斜杠转义 - 所以,“r”是一个两字符的字符串,包含一个反斜杠和一个小写的“n”。 Python的反斜杠的用法和正则表达式中的反斜杠之间有一个自然的碰撞,所以最好在Django中定义正则表达式的时候使用原始字符串。
简而言之,我们只是告诉Django,对URL/hello/的任何请求都应该由hello view函数来处理。 值得讨论这个URL模式的语法,因为它可能不会立即显而易见。 虽然我们想匹配URL/hell /,但是模式看起来却有些不同。 原因如下:
- Django在检查URL模式之前,从每个传入URL的前面删除斜杠。 这意味着我们的URLpattern不包含/ hello /中的前导斜杠。 起初,这可能看起来不直观,但是这个要求简化了一些东西 - 比如在其他URLconf中包含URLconf,我们将在第七章中介绍。
- 该模式包括脱字号(^)和美元符号($)。 这些是具有特殊含义的正则表达式字符:插入符的意思是“要求模式匹配字符串的开头”,美元符号的意思是“要求模式匹配字符串的末尾”。
这个概念最好通过例子来解释。如果我们使用了模式^ hello /(最后没有使用美元符号$结尾),那么任何以/hello/开头的URL都可以匹配,比如/hello/foo和/hello/bar,而不仅仅是/hello/。
类似地,如果我们删除了开始的脱字节符(即hello/$),Django会匹配任何以hello/结尾的URL,例如/foo/bar/hello/。如果我们只是使用hello/,没有脱字符^或美元符号$,那么任何包含hello/的URL都会匹配,比如/foo/hello/bar。因此,我们同时使用脱字符和美元符号来确保只有URL/hello/的匹配 - 不多不少。
你的大部分URL模式将以插入符号开头,并以美元符号结束,但具有执行更复杂匹配的灵活性是很好的。您可能想知道如果有人请求URL / hello会发生什么(也就是说,没有结尾的斜线)。因为我们的URL模式需要一个结尾的斜杠,该URL不匹配。然而,默认情况下,对URL不匹配URL的任何请求都不会以斜杠结尾,将被重定向到具有尾部斜杠的相同URL中(这受到APPEND_SLASH Django设置的限制,附录D)。
关于这个URLconf的另一个要注意的是,我们已经将hello view函数作为一个对象传递,而不用调用该函数。 这是Python(以及其他动态语言)的一个关键特性:函数是第一类对象,这意味着您可以像任何其他变量一样传递它们。 很酷的东西吧?
为了测试对URLconf的更改,像在第1章中一样,通过在Python虚拟环境中运行python manage.py runserver命令来启动Django开发服务器。 (如果让它继续运行,那也没关系,开发服务器会自动检测你的Python代码的变化,并根据需要重新加载,所以你不必在变化之间重新启动服务器。)
服务器运行的地址是http://127.0.0.1:8000/,因此请打开Web浏览器并转至http://127.0.0.1:8000/hello/。 您应该看到文本“Hello world” - Django视图的输出(图2-1)。
图2-1:你的第一个Django视图.png正则表达式
正则表达式(或正则表达式)是指定文本中模式的一种紧凑方式。 虽然Django的URLconf允许任意的正则表达式来进行强大的URL匹配,但是在实践中你可能只会使用一些正则表达式符号。 表2-1列出了常用符号的选择。正则表达式学习
Python正则表达式官方教程
元字符 | 描述 |
---|---|
\ | 将下一个字符标记符、或一个向后引用、或一个八进制转义符。例如,“\n”匹配\n。“\n”匹配换行符。序列“\”匹配“\”而“(”则匹配“(”。即相当于多种编程语言中都有的“转义字符”的概念。 |
^ | 匹配输入字行首。如果设置了RegExp对象的Multiline属性,^也匹配“\n”或“\r”之后的位置。 |
$ | 匹配输入行尾。如果设置了RegExp对象的Multiline属性,$也匹配“\n”或“\r”之前的位置。 |
* | 匹配前面的子表达式任意次。例如,zo能匹配“z”,也能匹配“zo”以及“zoo”。等价于o{0,} |
+ | 匹配前面的子表达式一次或多次(大于等于1次)。例如,“zo+”能匹配“zo”以及“zoo”,但不能匹配“z”。+等价于{1,}。 |
? | 匹配前面的子表达式零次或一次。例如,“do(es)?”可以匹配“do”或“does”中的“do”。?等价于{0,1}。 |
{n} | n是一个非负整数。匹配确定的n次。例如,“o{2}”不能匹配“Bob”中的“o”,但是能匹配“food”中的两个o。 |
{n,} | n是一个非负整数。至少匹配n次。例如,“o{2,}”不能匹配“Bob”中的“o”,但能匹配“foooood”中的所有o。“o{1,}”等价于“o+”。“o{0,}”则等价于“o*”。 |
{n,m} | m和n均为非负整数,其中n<=m。最少匹配n次且最多匹配m次。例如,“o{1,3}”将匹配“fooooood”中的前三个o为一组,后三个o为一组。“o{0,1}”等价于“o?”。请注意在逗号和两个数之间不能有空格。 |
[a-z] | 字符范围。匹配指定范围内的任意字符。例如,“[a-z]”可以匹配“a”到“z”范围内的任意小写字母字符。注意:只有连字符在字符组内部时,并且出现在两个字符之间时,才能表示字符的范围; 如果出字符组的开头,则只能表示连字符本身. |
关于404错误的快速注意
在这一点上,我们的URLconf只定义了一个URL模式:处理对URL / hello /的请求的URL模式。 当你请求一个不同的URL时会发生什么? 为了找到答案,请尝试运行Django开发服务器并访问如http://127.0.0.1:8000/goodbye/的页面。 你应该看到一个“找不到页面”的消息(图2-2)。 Django显示这个消息是因为你请求了一个没有在你的URLconf中定义的URL。
图2-2:Django的404页面.png此页面的实用程序超出了基本的404错误消息。 它还会精确地告诉你Django使用了哪个URLconf以及URLconf中的每个模式。 从这些信息,你应该能够知道为什么要求的URL扔404。
当然,这是一个敏感信息,只为你,网络开发者。 如果这是一个在互联网上实时部署的生产站点,那么您不希望将这些信息公开给公众。 出于这个原因,只有当Django项目处于调试模式时才会显示“页面未找到”页面。
稍后我将解释如何停用调试模式。 现在,只要知道每个Django项目在首次创建时都处于调试模式,而且如果项目不处于调试模式,则Django将输出一个不同的404响应。
关于网站根源的快速注意
Django不会神奇地向网站根目录添加任何东西; 该URL并不是特别的。 这意味着,如上一节所述,如果您查看站点根目录(http://127.0.0.1:8000/),则会看到404错误消息。
这取决于你将它分配给一个URL模式,就像URLconf中的其他条目一样。 但是,匹配站点根的URL模式有点不直观,所以值得一提。 当您准备好实现站点根目录的视图时,请使用匹配空字符串的URLpattern“^ $”。 例如:
from mysite.views import hello, my_homepage_view
urlpatterns = [
url(r'^$', my_homepage_view),
# ...
Django如何处理请求
在继续我们的第二个视图函数之前,让我们先停下来学习一下Django如何工作。 具体来说,当您通过浏览器访问http://127.0.0.1:8000/hello/来查看“Hello world”消息时,Django会在幕后做些什么?
这一切都从设置文件开始。 当您运行python manage.py runserver时,脚本会在内部mysite目录中查找名为settings.py的文件。 这个文件包含了这个Django项目的各种配置,全部用大写:TEMPLATES,DATABASES等。
最重要的设置叫做ROOT_URLCONF。 ROOT_URLCONF告诉Django哪个Python模块应该被用作这个网站的URLconf。 记得当Django的管理startproject创建文件settings.py和urls.py? 自动生成的settings.py包含一个指向自动生成的urls.py的ROOT_URLCONF设置。 打开settings.py文件,看看你自己; 它应该是这样的:
ROOT_URLCONF = 'mysite.urls'
这对应于文件mysite / urls.py。 当一个请求进入一个特定的URL时 - 例如,一个请求/ hello / - Django加载ROOT_URLCONF设置指向的URLconf。 然后,它会检查URLconf中的每个URL模式,以便将请求的URL与模式中的模式进行比较,直到找到匹配的模式。
当它找到一个匹配的时候,它会调用与该模式关联的视图函数,并将其作为第一个参数传递给一个HttpRequest对象。 (我们稍后会介绍HttpRequest的细节。)正如我们在第一个视图中看到的,视图函数必须返回一个HttpResponse。
一旦这样做,Django就完成剩下的工作,将Python对象转换为适当的HTTP响应头和body(即网页内容)的Web响应。 综上所述:
- 请求进入/ hello /。
- Django通过查看ROOT_URLCONF设置来确定根URLconf。
- Django查看第一个URLconf中的所有URL模式
匹配/hello/。 - 如果发现匹配,则调用相关的视图函数。
- 视图函数返回一个HttpResponse。
- Django将HttpResponse转换为正确的HTTP响应,结果将以网页形式呈现。
你现在知道如何制作Django驱动的页面的基础知识。 这很简单,只需编写视图函数并通过URLconf将它们映射到URL即可。