正如我们的第一个目标,创建一个网页,用来输出这个著名的示例信息:
Hello world
如果你曾经发布过 Hello world 页面,但是没有使用网页框架,只是简单的在hello.html
文本文件中输入
Hello World,然后上传到任意的一个网页服务器上。注意,在这个过程中,你已经说明了两个关于这个网页的关键信息:它包括(字符串 "Hello world"
)和它的URL(https://www.birdpython.com/hello.html
, 如果你把文件放在子目录,也可能是
https://www.birdpython.com/files/hello.html
)。
使用 Django,你会用不同的方法来说明这两件事,页面的内容是靠 view function(视图函数) 来产生,URL定义在 URLconf 中。首先,我们先写一个 Hello World 视图函数。
在上一章使用django-admin.exe startproject
创建的 mysite
项目中的 mysite 文件夹下,新建一个叫做
views.py
的空文件。这个 Python 模块将包含这一章的视图。请留意,Django 对于
view.py 的文件命名没有特别的要求,它不在乎这个文件叫什么。但是根据约定,把它命名成
view.py 是个好主意,这样有利于其他开发者读懂你的代码,正如你很容易的往下读懂本文。
我们的 Hello world 视图非常简单。这些是完整的函数和导入声明,你需要输入到 views.py
文件:
from django.http import HttpResponse def hello(request): return HttpResponse("Hello world")
我们逐行逐句地分析一遍这段代码:
首先,我们从django.http
模块导入(import)HttpResponse
类。我们需要导入这些类,因为我们会在后面用到。接下来,我们定义一个叫做hello
的视图函数。每个视图函数至少要有一个参数,通常被叫作request
。 这是一个触发这个视图、包含当前 Web 请求信息的对象,是类django.http.HttpRequest
的一个实例。在这个示例中,我们虽然不用request
做任何事情,然而它仍必须是这个视图的第一个参数。注意视图函数的名称并不重要;并不一定非得以某种特定的方式命名才能让 Django 识别它。 在这里我们把它命名为:hello,是因为这个名称清晰的显示了视图的用意。同样地,你可以用诸如:hello_wonderful_beautiful_world,这样难看的短句来给它命名。在下一小节(Your First URLconf),将告诉你 Django 是如何找到这个函数的。这个函数只有简单的一行代码:它仅仅返回一个 HttpResponse 对象,这个对象包含了文本“Hello world”。
这里主要讲的是:一个视图就是 Python 的一个函数。这个函数第一个参数的类型是 HttpRequest;它返回一个 HttpResponse 实例。为了使一个 Python 的函数成为一个 Django 可识别的视图,它必须满足这两个条件。(也有例外,但是我们稍后才会接触到)。
现在,如果你再运行:python manage.py runserver
,你还将看到 Django
的欢迎页面,而看不到我们刚才写的 Hello world 显示页面。那是因为我们的 mysite 项目还对 hello
视图一无所知。我们需要通过一个详细描述的 URL
来显式的告诉它并且激活这个视图。(继续我们刚才类似发布静态 HTML
文件的例子。现在我们已经创建了 HTML 文件,但还没有把它上传至服务器的目录。)为了绑定视图函数和
URL,我们使用 URLconf。
URLconf 就像是 Django 所支撑网站的目录。它的本质是 URL 模式以及要为该 URL 模式调用的视图函数之间的映射表。你就是以这种方式告诉 Django,对于这个 URL 调用这段代码,对于那个 URL 调用那段代码。例如,当用户访问 /foo/ 时,调用视图函数 foo_view(),这个视图函数存在于 Python 模块文件 view.py 中。
前一章中执行 django-admin.py startproject
时,该脚本会自动为你建了一份
URLconf(即 urls.py
文件)。默认的 urls.py 会像下面这个样子:
"""mysite URL Configuration The `urlpatterns` list routes URLs to views. For more information please see: https://docs.djangoproject.com/en/2.2/topics/http/urls/ Examples: Function views 1. Add an import: from my_app import views 2. Add a URL to urlpatterns: path('', views.home, name='home') Class-based views 1. Add an import: from other_app.views import Home 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') Including another URLconf 1. Import the include() function: from django.urls import include, path 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ from django.contrib import admin from django.urls import path urlpatterns = [ path('admin/', admin.site.urls), ]
让我们逐行解释一下代码:
第一行导入 from django.contrib
下的 admin 模块,它们是 Django 自带的后台管理系统,当然你也可以不使用这个,自己来编写后台管理系统。
第二行导入 from django.urls
下的 path 模块,该模块来解析域名(比如用户输入:127.0.0.1:8000/admin/)后面的字符串(此处为:admin)所对应的的函数(此处为:admin.site.urls)。
当前应该注意是 urlpatterns
变量,Django 期望能从 ROOT_URLCONF
模块中找到它。该变量定义了 URL 以及用于处理这些 URL 的代码之间的映射关系。默认情况下,URLconf
所有内容都被注释起来了——Django 应用程序还是白版一块。(注:那是上一节中 Django
怎么知道显示欢迎页面的原因。如果 URLconf 为空,Django 会认定你才创建好新项目,因此也就显示那种信息。
如果想在 URLconf 中加入 URL 和 view,只需增加映射 URL 模式和 view 功能的 Python tuple 即可。这里演示如何添加 view 中 hello 功能.
from django.contrib import admin from django.urls import path from django.urls import re_path # 新增代码 from mysite.views import hello # 新增代码 urlpatterns =[ path('admin/', admin.site.urls), re_path('^hello/$', hello), # 新增代码 ]
请留意:为了简洁,我们移除了注释代码。如果你喜欢的话,你可以保留那些行。
我们做了 3 处修改。
首先,我们从模块 (在 Python 的 import 语法中, mysite/views.py
转译为 mysite.views
) 中引入了 hello
视图。(这假设 mysite/views.py 在你的 Python 搜索路径上。关于搜索路径的解释,请参照下文。)
接下来,我们从模块 from django.urls
中导入了 re_path 函数对象;该函数对象可以对接收的参数进行模式匹配(正则表达式)。
最后,我们为 urlpatterns 加上一行: re_path('^hello/$', hello),它是一个 Python 的元组。元组中第一个元素是模式匹配字符串(正则表达式);第二个元素是那个模式将使用的视图函数。
简单来说,我们只是告诉 Django,所有指向 URL /hello/
的请求都应由 hello
这个视图函数来处理。
path 只对接收的参数做精确匹配,而 re_path 可以对接收的参数做模糊匹配,这样更满足我们的需求。
讨论一下 re_path 的语法是值得的,因为它不是显而易见的。虽然我们想匹配地址 /hello/,但是模式看上去与这有点差别。这就是为什么:
Django 在检查 URL 模式前,移除每一个申请的 URL 开头的斜杠(/)。这意味着我们为 /hello/ 写 URL 模式不用包含斜杠(/)。(刚开始,这样可能看起来不直观,但这样的要求简化了许多工作,如 URL 模式内嵌)模式包含了一个尖号(^)和一个美元符号($)。这些都是正则表达式符号,并且有特定的含义: 上箭头要求表达式对字符串的头部进行匹配,美元符号则要求表达式对字符串的尾部进行匹配。最好还是用范例来说明一下这个概念。 如果我们用尾部不是$的模式’^hello/’,那么任何以 /hello/ 开头的URL将会匹配,例如:/hello/foo 和 /hello/bar,而不仅仅是/hello/。类似地,如果我们忽略了尖号(^),即 'hello/$',那么任何以 hello/ 结尾的 URL 将会匹配,例如:/foo/bar/hello/。如果我们简单使用 hello/,即没有 ^ 开头和 $ 结尾,那么任何包含 hello/ 的 URL 将会匹配,如:/foo/hello/bar。因此,我们使用这两个符号以确保只有 /hello/ 匹配,不多也不少。你大多数的 URL 模式会以 ^ 开始、以 $ 结束,但是拥有复杂匹配的灵活性会更好。你可能会问:如果有人申请访问 /hello(尾部没有斜杠/)会怎样。因为我们的URL模式要求尾部有一个斜杠(/),那个申请URL将不匹配。然而,默认地,任何不匹配或尾部没有斜杠(/)的申请 URL,将被重定向至尾部包含斜杠的相同字眼的URL。(这是受配置文件 setting 中 APPEND_SLASH 项控制的。)如果你是喜欢所有 URL 都以 '/' 结尾的人(Django 开发者的偏爱),那么你只需要在每个 URL 后添加斜杠,并且设置 ”APPEND_SLASH” 为 ”True”。如果不喜欢 URL 以斜杠结尾或者根据每个 URL 来决定,那么需要设置 ”APPEND_SLASH” 为 ”False”,并且根据你自己的意愿来添加结尾斜杠 / 在 URL 模式后。
另外需要注意的是,我们把 hello 视图函数作为一个对象传递,而不是调用它。这是 Python(及其它动态语言的) 的一个重要特性:函数是一级对象(first-class objects),也就是说你可以像传递其它变量一样传递它们。很酷吧?
启动 Django 开发服务器来测试修改好的 URLconf, 运行命令行 python manage.py runserver
。(如果你让它一直运行也可以,开发服务器会自动监测代码改动并自动重新载入,所以不需要手工重启)开发服务器的地址是 http://127.0.0.1:8000/ ,打开你的浏览器访问 http://127.0.0.1:8000/hello/ 。你就可以看到输出结果了。开发服务器将自动检测Python代码的更改来做必要的重新加载, 所以你不需要重启Server在代码更改之后。服务器运行地址 http://127.0.0.1:8000/
,所以打开浏览器直接输入 http://127.0.0.1:8000/hello/
,你将看到由你的 Django 视图输出的 Hello world。
万岁!我们已经创建了第一个 Django 的 web 页面。
正则表达式(或 regexes) 是通用的文本模式匹配的方法。Django URLconfs 允许你 使用任意的正则表达式来做强有力的URL映射,不过通常你实际上可能只需要使用很少的一 部分功能。这里是一些基本的语法。
符号 | 匹配 |
---|---|
. (dot) |
任意单一字符 |
\d |
任意一位数字 |
[A-Z] |
A 到 Z 中任意一个字符(大写) |
[a-z] |
a 到 z 中任意一个字符(小写) |
[A-Za-z] |
a 到 z 中任意一个字符(不区分大小写) |
+ |
匹配一个或更多 (例如, \d+ 匹配一个或 多个数字字符) |
[^/]+ |
一个或多个不为‘/’的字符 |
? |
零个或一个之前的表达式(例如:\d? 匹配零个或一个数字) |
* |
匹配0个或更多 (例如, \d* 匹配0个 或更多数字字符) |
{1,3} |
介于一个和三个(包含)之前的表达式(例如,\d{1,3} 匹配一个或两个或三个数字) |
有关正则表达式的更多内容,请访问 http://www.djangoproject.com/r/python/re-module/.
Python 搜索路径 就是使用 import
语句时,Python 所查找的系统目录清单。
安装到 Python 解释器下的 Django 模块同样属于第三方模块,第三方模块一般都是安装到和解释器同目录下的 lib/site-packages
目录下(关于第三方模块安装路径问题,请参考 模块路径搜索)。由于我们的
Django 是安装到虚拟环境下,所有 Django 相应的库就放在了我们虚拟化环境下解释器的 /lib/site-packages
目录下(我的具体为 C:/Python3/Scripts/djangoenv/Lib/site-packages
)。
举例来说,假定你将 Python 路径设置为 ['','C:/Python3/Scripts/djangoenv/Lib/site-packages','E:/djproject/mysite/']
。如果执行代码
from foo import bar
,Python 将会首先在当前目录查找 foo.py
模块( Python 路径第一项的空字符串表示当前目录)。
如果文件不存在,Python 将查找 C:/Python3/Scripts/djangoenv/Lib/site-packages/foo.py
文件,如果再查不到,Python
将查找 E:/djproject/mysite/foo.py
。如果最后查遍了整个 Python
搜索路径下设置的值都没有查到,则会报错找不到该模块。
如果你想看 Python 搜索路径的值,运行 Python 交互解释器,然后输入:
>>> import sys >>> print sys.path
通常,你不必关心 Python 搜索路径的设置。Python 和 Django 会在后台自动帮你处理好。
如果你想更详细的了解 Python 搜索路径的规则,请参考 模块路径搜索。
目前,我们的 URLconf 只定义了一个单独的 URL 模式:处理 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/
(网站根目录)。你将会看到一个 “Page not found” 页面(图
3-2)。因为你的 URL 申请在 URLconf 中没有定义,所以 Django 显示这条信息。
这个页面比原始的 404 错误信息更加实用。它同时精确的告诉你 Django 调用哪个 URLconf 及其包含的每个模式。这样,你应该能了解到为什么这个请求会抛出 404 错误。
当然,这些敏感的信息应该只呈现给你-开发者。如果是部署到了因特网上的站点就不应该暴露这些信息。出于这个考虑,这个“Page not found”页面只会在 调试模式(debug mode) 下显示。 我们将在以后说明怎么关闭调试模式。
在最后一节,如果你想通过http://127.0.0.1:8000/,看网站根目录你将看到一个404错误消息。Django不会增加任何东西在网站根目录,在任何情况下这个URL都不是特殊的看网站根目录你将看到一个404错误消息。Django不会增加任何东西在网站根目录,在任何情况下这个URL都不是特殊的 就像在 URLconf 中的其他条目一样,它也依赖于指定给它的 URL 模式。
尽管匹配网站根目录的 URL 模式不能想象,但是还是值得提一下的。当为网站根目录实现一个视图,你需要使用URL模式‘^$’
, 它代表一个空字符串。例如:
from mysite.views import hello, my_homepage_view urlpatterns =[ path('admin/', admin.site.urls), re_path ('^$', my_homepage_view), ]
在继续我们的第二个视图功能之前,让我们暂停一下去了解更多一些有关 Django 是怎么工作的知识。具体地说,当你通过在浏览器里敲 http://127.0.0.1:8000/hello/ 来访问 Hello world 消息得时候,Django 在后台有些什么动作呢?
所有均开始于 setting 文件。当你运行 python manage.py runserver,脚本将在于 manage.py 同一个目录下查找名为 setting.py 的文件。这个文件包含了所有有关这个 Django项目的配置信息,均大写:TEMPLATE_DIRS,DATABASE_NAME 等。最重要的设置时 ROOT_URLCONF,它将作为 URLconf 告诉 Django 在这个站点中那些 Python 的模块将被用到。
还记得什么时候 django-admin.py startproject 创建文件 settings.py 和 urls.py 吗?自动创建的 settings.py 包含一个 ROOT_URLCONF 配置用来指向自动产生的 urls.py。 打开文件 settings.py 你将看到如下:
ROOT_URLCONF = 'mysite.urls'
相对应的文件是 mysite/urls.py
当访问 URL /hello/
时,Django 根据 ROOT_URLCONF
的设置装载
URLconf。然后按顺序逐个匹配 URLconf 里的 URLpatterns,直到找到一个匹配的。当找到这个匹配的
URLpatterns 就调用相关联的 view 函数,并把 HttpRequest
对象作为第一个参数。(稍后再给出 HttpRequest
的更多信息)(我们将在后面看到
HttpRequest 的标准)
正如我们在第一个视图例子里面看到的,一个视图功能必须返回一个 HttpResponse。一旦做完,Django 将完成剩余的转换 Python 的对象到一个合适的带有 HTTP 头和 body 的 WebResponse,(例如,网页内容)。
总结一下:
进来的请求转入 /hello/。
Django 通过在 ROOT_URLCONF 配置来决定根 URLconf。
Django 在 URLconf 中的所有 URL 模式中,查找第一个匹配 /hello/ 的条目。
如果找到匹配,将调用相应的视图函数。
视图函数返回一个 HttpResponse。
Django 转换 HttpResponse 为一个适合的 HTTP response,以 Web page 显示出来
你现在知道了怎么做一个 Django-powered 页面了,真的很简单,只需要写视图函数并用 URLconfs 把它们和 URLs 对应起来。你可能会认为用一系列正则表达式将URLs映射到函数也许会比较慢,但事实却会让你惊讶。