Django 速成:构建一个Blog
Django 自称是“最适合开发有限期的完美Web 框架”。所以现在我们来挑战一下,看看到底能在多短的时间里用Django 完成一个简单的blog 。(关于追求完美的部分我们稍后再细说。)
注意
本章假设你已经安装了Django 。如果还没有的话,请参见附录B 。
本章所有工作都是在你选择的shell (bash 、tcsh 、zsh 和Cygwin 等)下以命令行方式完成。所以,首先打开终端,键入cd 命令进入你PYTHONPATH 环境变量所指向的目录。在Linux 、Mac OS X 、FreeBSD 等类UNIX 的系统上,你可以用echo $PYTHONPATH 命令来查看其内容。在Win32 的命令行窗口里,则可以输入echo %PYTHONPATH% 。你可以在第1 章里读到更多关于路径和安装的内容。
我们建议你在阅读的同时实际动手构建这个blog 。如果做不到(例如不在电脑旁边,或者没这个耐性)的话,简单读一下本章也是好的。特别是如果你有其他现代Web 框架开发的经验就更好了,因为很多基本的概念都是相似的。
如果你确实打算在电脑上实际操作,同时遇到和你在这里看到的结果不同的情况时,请停下来重新检验刚才的步骤,然后复查之前的两到三步。看看是不是有哪些看上去不重要的地方,或是哪些不能理解的步骤被遗漏掉了。如果还是没发现哪里不对,那就删掉整个项目重新来过。作者在学习Django 时就用过这个办法;这样比无助地盯着错误信息看上几个小时要有效率,而且重复那些会导致错误的步骤还能帮你加深印象!
2.1 创建项目
组织Django 代码最简单的方式是使用Django 的“项目”(project ):一个包含了组成单个网站的所有文件的目录。Django 提供了一个叫django-admin.py 的命令来帮助创建这样项目的目录。在UNIX 上,它的默认安装路径为/usr/bin ;在Win32 上,它的位置是Python 安装目录下的Scripts 文件夹,比如C:/Python25/Scripts 。无论是哪一个平台,你都要保证django-admin.py 在PATH 环境变量里以保证它可以从命令行执行。
为blog 项目创建一个项目目录的django-admin.py 命令是:
在Windows 上,你需要先打开一个DOS 命令窗口。可以通过【开始→程序→附件→命令提示符】来启动这个窗口。另外,你会看到C:/WINDOWS/system32 这样的提示符,而不是$ 符号。
现在我们来看看这个命令为你创建的目录里有点什么内容。在UNIX 上它看起来是这样的:
如果你是在Win32 平台上进行开发,那么打开资源管理器窗口你可以看到图2.1 这样的文件夹,这里我们所创建的目录是C:/py/django ,项目的内容将会存放在这里。
图2.1 Win32 上的mysite 文件夹
注意
如果你精通Python 的话,你一定知道__init__.py 会把这个项目目录变成一个Python 的包(package )—相关Python 模块的一个集合。这让我们可以用Python 的“点记号”(dot-notation )来指定项目中的某个部分,比如mysite.urls 。(你可以阅读第1 章,以获取更多关于包的内容。)
除了__init__.py ,startproject 命令还创建了以下三个文件:
?manage.py 文件是一个同这个Django 项目一起工作的工具。你可以从它在目录列表中的权限里看到它是可以执行的。我们马上就会用到它。
?settings.py 文件包含了项目的默认设置。包括数据库信息、调试标志以及其他一些重要的变量。你项目里安装的任何应用都可以访问这个文件。稍后在本章进行过程中我们会展示其更多的作用。
?urls.py 文件在Django 里叫URLconf ,它是一个将URL 模式映射到你应用程序上的配置文件。URLconf 是Django 里非常强大的一个特性。
注意
startproject 命令创建的所有文件都是Python 的源码文件。这里没有XML 、.ini 文件,或是任何时髦的配置语法。Django 追求的是尽可能地保持“纯Python ”这一理念。这让你在拥有诸多灵活性的同时无需在框架里引入任何复杂性。例如,如果你想让你的settings 文件从其他文件导入设置,或是计算一个值而避免硬编码的话都可以畅行无阻—因为它就是Python 而已。
2.2 运行开发服务器
到这里,你还没有构建完blog 应用,不过Django 为你提供了一些可以就地使用的方便。其中最好用的就是Django 的内置Web 服务器了。这个服务器不是用来部署公共站点,而是用来做快速开发的。其优点在于:
? 不需要安装Apache 、Lighttpd ,或是其他任何实际生产所需的Web 服务器软件—如果你在一台新的服务器或是没有服务器环境的开发机上,或是就想实验一下的话,那就非常方便了。
? 它会自动检测到你对Python 源码的修改并且重新加载那些模块。相比每次修改代码后都要手动地重启Web 服务器可是大大地节约了不少时间,这对绝大多数Python 的Web 服务器设置来说都是很有必要的。
? 它知道如何为admin 应用程序寻找并显示静态的媒体文件,所以你就可以直接使用它。
运行开发服务器(或“dev ”)简单到只需要一个命令就行了。这里我们要用到项目里的manage.py 工具,这是一个简单的包裹脚本,能直接告诉django-admin.py 去读入项目特定的settings 文件。启动dev 的命令如下:
在Win32 平台你应该能看到类似如下的输出,只是要退出时要按下的快捷键是Ctrl+Break 而不是Ctrl+C 。
在浏览器里输入这个链接,你会看到Django 的“It worked! ”页面,如图2.2 所示。
图2.2 Django 初始的“It worked! ”页面
同时,如果你查看终端的话,你会看到dev 服务器记录下了你的GET 请求。
log 从左到右有4 个部分:时间戳、请求、HTTP 状态码,以及字节数。(你看到的字节数或许会有所不同。)这里状态码404 (“Not Found ”)是因为还没有为项目定义任何URL 。而“It worked! ”页面只是Django 委婉地告诉你这一点的方式。
提示
如果服务器不工作的话,请重新检查每一个步骤。不要怕麻烦!有时候直接删掉整个项目,然后重新开始可能反而比劳心劳力地检查每一个文件,每一行代码要来的简单。
如果你成功启动服务器后,我们就可以来设置你的第一个Django 应用了。
2.3 创建Blog 应用
有了项目以后,就可以在它下面创建应用(按Django 的说法是“app ”)了。我们再次使用manage.py 来创建这个blog app 。
这样就完成项目的创建了。现在我们在项目的目录下有了一个blog 目录。这是它的内容,首先是UNIX 下的,接着是Windows 资源管理器里的截图(图2.3 )。
图2.3 Win32 上的mysite/blog 文件夹
和项目一样,app 也是一个包。现在models.py 和views.py 里还没有真正的代码,它们只是先占住位子而已。实际上对我们这个简单的blog 来说,我们根本用不着views.py 文件。
要告诉Django 这个app 是项目里的一部分,你需要去编辑settings.py 文件(也称之为“配置文件”)。打开配置文件并在文件尾部找到INSTALLED_APPS 元组。把你的app 以模块的形式添加到元组里,就像这样(注意结尾的逗号):
Django 用INSTALLED_APPS 来决定系统里不同部分的配置,包括自动化的admin 应用以及测试框架。
2.4 设计你的Model
现在我们来到了这个基于Django 的blog 应用的核心部分:models.py 文件。这是我们定义blog 数据结构的地方。根据DRY 原则,Django 会尽量利用你提供给应用程序的model 信息。我们先来创建一个基本的model ,看看Django 用这个信息为我们做了点什么。
用你最习惯的编辑器(如果它能支持Python 模式就最好)打开models.py 文件。你会看到这样的占位文本:
删掉注释,加入下列代码:
这是一个完整的model ,代表了一个有3 个变量的“BlogPost ”对象。(严格说来应该有4 个,Django 会默认为每个model 自动加上一个自增的、唯一的id 变量。)
这个新建的BlogPost 类是django.db.models.Model 的一个子类。这是Django 为数据model 准备的标准基类,它是Django 强大的对象关系映射(ORM )系统的核心。此外,每一个变量都和普通的类属性一样被定义为一个特定变量类(field class )的实例。这些变量类也是在django.db.models 里定义,它们的种类非常多,从BooleanField 到XMLField 应有尽有,可不止这里看到的区区三个。
2.5 设置数据库
如果你没有安装运行数据库服务器的话,我们推荐使用SQLite ,这是最快最简单的办法。它的速度很快,被广泛接受,并且将它的数据库作为一个文件存放在文件系统上。访问控制就是简单的文件权限。更多关于在Django 里使用数据库的信息请参阅附录B 。
如果你安装了数据库服务器(PostgreSQL 、MySQL 、Oracle 和MSSQL ),并且希望用它们而非SQLite 的话,你需要用相应的数据库管理工具为Django 项目创建一个新的数据库。在这里我们把数据库取名为“djangodb ”,不过你可以选用任何喜欢的名字。
当你有了一个(空的)数据库以后,接下来只需要告诉Django 如何使用它即可。这就需要用到项目的settings.py 文件。
使用数据库服务器
很多人都选用PostgreSQL 或MySQL 这样的关系数据库和Django 配合使用。这里有6 个相关的设置(虽然你可能只要两个就够了):DATABASE_ENGINE 、DATABASE_NAME 、DATABASE_HOST 、DATABASE_PORT 、DATABASE_USER 和DATABASE_PASSWORD 。它们的作用从名字上就能看的出来。你只需在相应的位置填入正确的值就可以了。例如,为MySQL 的设定看上去是这样的:
注意
我们没有指定DATABASE_PORT 是因为只有当你的数据库服务器运行在非标准的端口上时才需要那么做。比如,MySQL 服务器的默认端口为3306 。除非你修改了这个设置,否则根本不用指定DATABASE_PORT 。
要知道创建新数据库和(数据库服务器要求的)数据库用户的细节,请参阅附录B 。
使用SQLite
SQLite 非常适合测试,甚至可以部署在没有大量并发写入的情况下。因为SQLite 使用本地文件系统作为存储介质并且用原生的文件系统权限来做访问控制,像主机、端口、用户或密码这种信息一律统统不需要。因此Django 只要知道以下的两个设置就能使用你的SQLite 数据库了。
注意
当SQLite 配合Apache 这样真正的Web 服务器一起使用时,你需要确认拥有Web 服务器进程的账号也拥有对数据库文件以及包含数据库文件目录的写权限。在我们这里用dev 服务器做开发的情况下,这通常不是一个问题,因为运行dev 服务器的用户(你)同时也拥有项目的文件和目录。
SQLite 在Win32 平台上也是非常受欢迎的选择之一,因为它是随同Python 一同免费发布的。假设我们已经为项目(和应用程序)创建了C:/py/django 目录,现在再来创建一个db 目录。
如果你不太熟悉Python 的话,你会注意到这个例子和前一个例子的不同之处,之前我们在sqlite3 上用的是双引号,而在Win32 的版本里,我们用的是单引号。这个和平台是没有关系的—Python 没有字符类型,所以单引号和双引号都是被等同对待。只要保证你用同样的引号引用字符串就可以了!
你还应该注意到了文件夹名字前的小写“r ”。如果你没有读过第一章,那么你应该知道它的意思是这个对象是一个raw 字符串,或者说它把字符串里所有字符都逐字保留下来,不对任何特殊字符组合做转义。例如,/n 通常代表一个新行,但是在raw 字符串里,它(字面上)代表了两个字符:一个反斜杠和一个小写n 。所以raw 字符串的作用(特别是对这里的DOS 文件路径来说)就是告诉Python 不要转义任何特殊字符(如果有的话)。
创建表
现在你可以告诉Django 用你提供的连接信息去连接数据库并且设置应用程序所需的表。命令很简单:
在Django 设置数据库的过程中你会看到类似这样的输出:
当你执行syncdb 命令时,Django 会查找INSTALLED_APPS 里的每一个models.py 文件,并为找到的每一个model 都创建一张数据库表。(在稍后我们碰到华丽的多对多关系时会有例外,但是对这个例子来说是正确的。如果你用的是SQLite ,你还可以看到django.db 数据库会在你指定的位置上被创建出来。)
INSTALLED_APPS 里的其他默认条目也都拥有model 。manage.py syncdb 的输出就确认了这一点,你可以看到Django 为每个app 都创建了一个或多个表。
但这还不是syncdb 命令输出的全部。你还会被问到一些和django.contrib.auth app 有关的问题。
现在你在auth 系统里建立了一个超级用户(就是你自己)。这在等一会我们加入Django 的自动admin 应用时就会变得很方便。
最后,这一过程还包裹了一些和特性fixture 有关的代码,在第4 章里我们在讨论这个。这让你在一个新建立的应用里预先载入数据。我们在这里没有用到这个特性,所以Django 会跳过它。
到此数据库的初始化就完成了,下次你再对这个项目运行syncdb 命令(只要你添加了应用或是model 时就要执行)时,就不会再看到那么多输出了,因为它不需要再次设置表或者提示你创建超级用户了。
2.6 设置自动admin 应用
自动化的后台应用程序admin 称得上是Django “皇冠上的明珠”。任何对为Web 应用创建简单的“CRUD ”(Create ,Read ,Update ,Delete )接口感到厌倦的人来说,这绝对是喜从天降。我们会在第11.1 节里深入讨论admin 。现在我们只要拿来直接用就好了。
由于这不是Django 的必要组件,你必须在settings.py 文件里指定你要使用它—就和指定blog app 一样。打开settings.py 并在INSTALLED_APPS 元组里的'django.contrib.auth' 下面添加这样一行。
每次往项目里添加新的应用后,你都要运行一下syncdb 命令确保它所需的表已经在数据库里创建了。在这里可以看到在INSTALLED_APPS 里添加admin app 并运行syncdb 命令会往我们的数据库里加入一个新的表:
设置完app 以后,我们需要为它指定一个URL 这样才能访问它。你应该已经注意到了在自动生成的urls.py 中的这样两行代码。
删掉第二行开头的# 号(也可以同时删掉第一行)并保存文件。这样你就告诉Django 去加载默认的admin 站点,这是被用于contrib admin 应用程序的一个特殊对象。
最后,你的应用程序需要告诉Django 要在admin 窗口里显示哪一个model 以供编辑。要做到这一点很简单,只要定义之前提到的默认admin 站点,并向其注册BlogPost model 就行了。打开mysite/blog/models.py 文件,确认导入了admin 应用,然后在最后加上一行注册model 的代码。
这里admin 的简单使用只不过是冰山一角,它还可以通过为给定model 建立一个特殊的Admin 类来指定许多不同和admin 有关的选项,然后向那个类注册model 。这个我们稍后再说,在后面的章节里你还会看到admin 的高级使用,特别是本书第三部分和第四部分。
2.7 试用admin
到这里我们已经在Django 里设置了admin app 并向其注册了model ,现在是时候试一试它了。再次运行manage.py runserver 命令,随后在浏览器里输入http://127.0.0.1:8000/admin/ 。(你的dev 服务器可能不是这个地址,没关系,只要在后面加上admin/ 就可以了。)你应该会看到一个登录窗口,如图2.4 所示。
输入你之前创建的“超级用户”名字和密码。登录后,你就可以看到admin 的主页了,如图2.5 所示。
图2.4 admin 登录页面
图2.5 admin 主页
本书稍后会解释这个界面,现在只需确认你的应用程序Blog 和截图里一样出现在屏幕上。如果没有的话,请重新检查之前的步骤。
提示
三个最常见的“我的app 没有显示在admin 里”的原因是:1 )忘记向admin.site.register 注册你的model 类;2 )models.py 里有错误;以及3 )忘记在settings.py 中的INSTALLED_ APPS 里添加app 。
有没有内容的blog 么?点击Blog Posts 右侧的Add 按钮。Admin 会显示一个表单让你添加新的帖子,如图2.6 所示。
图2.6 通过admin 添加新内容
给你的帖子取一个名字然后往里填一点内容。至于时间戳,你可以点击Today 和Now 的快捷链接来获取当前的日期和时间。你还可以点击日历或时钟标志来方便地选择时间。
完成之后,点击Save 按钮保存。你会收到一条确认消息(“The blog post 'BlogPost object' was added successfully. ”)和一个列出你所有blog 帖子的列表—目前只有一篇而已,如图2.7 所示。
为什么帖子有“BlogPost object ”这么难看的名字?Django 的设计是希望能灵活地处理任意类型的内容,所以它不会去猜测对于给定的内容什么变量才是最合适的。在第三部分的例子里,你会看到如何为你对象的默认标签指定一个特定变量,或是特别定制的字符串。
现在点击右上角的“Add Blog Post + ”按钮添加另一篇不同内容的帖子。当你回到列表视图里,你会看见页面上加入了另一个BlogPost 。如果你刷新页面或是离开应用程序再回来的话,输出不会有任何变化—你一定不会喜欢看见所有条目都被标为“BlogPost object ”,如图2.8 所示。你不是第一个这么想的人,“总有办法让它看起来顺眼一点吧。”
图2.7 成功地保存你的第一篇blog
图2.8 不太有用的小节页面
不过我们不需要等到那个时候来清理admin 视图里的这些列表显示。之前我们通过很少的配置就激活了admin 工具,即向admin app 注册我们的model 。现在只要额外的两行代码以及对注册调用的一些修改,我们就可以让列表看起来更漂亮更有用。更新你的mysite/blog/models. py 文件,添加一个BlogPostAdmin 类,并将它加到注册代码那一行里,于是现在models.py 看起来是这样的:
开发服务器会注意到你的修改并自动重新加载model 文件。如果你去看一下命令行shell ,就能看到这些效果。
刷新一下页面,现在你就可以看到一个更有意义的页面了,它是根据你添加到BlogPostAdmin 类里list_display 变量来生成输出的,如图2.9 所示。
图2.9 这样就好多了
试着点一下Title 和Timestamp 列的标题—每一个都影响了你的条目是如何排序的。例如,点一下Title 会按照升序排列标题,再点一下则变成降序排列。
Admin 还有很多有用的特性只需一到两行代码就可以激活:搜索、自定义排序、过滤等。就像我们多次提到的,在本书第三和第四部分里会更详细地展示这些内容。
2.8 建立Blog 的公共部分
完成我们应用的数据库部分和admin 部分后,现在来看看面向公众的页面部分。从Django 的角度来说,一个页面具有三个典型的组件:
? 一个模板(template ),模板负责将传递进来的信息显示出来(用一种类似Python 字典的对象Context )。
? 一个视图(view )函数,它负责获取要显示的信息,通常都是从数据库里取得。
? 一个URL 模式,它用来把收到的请求和你的视图函数匹配,有时也会向视图传递一些参数。
创建模板
Django 的模板语言相当简单,我们直接来看代码。这是一个简单的显示单个blog 帖子的模板:
它就是一个HTML (虽然Django 模板可以用于任何形式的输出)加上一些大括号里的特殊模板标签。这些是变量标签(variable tag ),用于显示传递给模板的数据。在变量标签里,你可以用Python 风格的dotted-notation (点记号)来访问传递给模板的对象的属性。例如,这里假设你传递了一个叫“post ”的BlogPost 对象。这三行模板代码分别从BlogPost 对象的title 、timestamp 和body 变量里获取了相应的值。
现在我们稍微改进一下这个模板,通过Django 的for 模板标签让它能显示多篇blog 帖子。
原来的3 行没有动,我们只是简单地增加了一个叫做for 的块标签(block tag ),用它将模板渲染到序列中的每个元素上。其语法和Python 的循环语法是一致的。注意和变量标签不同,块标签是包含在{% ... %} 里的。
把这5 行模板代码保存到文件archive.html 里,然后把文件放到你的blog app 目录里的templates 目录下。这个文件的路径即为:
模板本身的名字是随意取的(叫它foo.html 也没问题),但是templates 目录的名字则是强制的。Django 在默认情况下会在搜索模板时逐个查看你安装的应用程序下的每一个templates 目录。
创建一个视图函数
现在我们来编写一个从数据库读取所有blog 帖子的视图函数,并用我们的模板将它们显示出来。打开blog/views.py 文件并输入:
先略过import 那几行(它们载入了我们需要的函数和类),我们来逐行解释一下这个视图函数。
? 第5 行:每个Django 视图函数都将django.http.HttpRequest 对象作为它的第一个参数。它还可以通过URLconf 接受其他参数,你将来会大量用到这个特性。
? 第6 行:当我们把BlogPost 类作为django.db.model.Model 的一个子类时,我们就获得了Django 对象关系映射的全部力量。这一行只是使用ORM (对象关系映射,详见第3 章和第4 章)的一件简单例子,获取数据库里所有BlogPost 对象。
? 第7 行:这里我们只需告诉Django 模板的名字就能创建模板对象t 。因为我们把它保存在app 下的templates 目录里,Django 无需更多指示就能找到它。
? 第8 行:Django 模板渲染的数据是由一个字典类的对象context 提供的,这里的context c 只有一对键和值。
? 第9 行:每个Django 视图函数都会返回一个django.http.HttpResponse 对象。最简单的就是给其构造函数传递一个字符串。这里模板的render 方法返回的正是一个字符串。
创建一个URL 模式
我们的页面还差一步就可以工作了—和任何网页一样,它还需要一个URL 。
当然我们可以直接在mysite/urls.py 里创建所需的URL 模式,但是那样做只会在项目和app 之间制造混乱的耦合。Blog app 还可以用在别的地方,所以最好是它能为自己的URL 负责。这需要两个简单的步骤。
第一步和激活admin 很相似。在mysite/urls.py 里有一行被注释的示例几乎就是我们需要的代码。把它改成这样:
这会捕捉任何以blog/ 开始的请求,并把它们传递给一个你马上要新建的URLconf 。
第二步是在blog 应用程序包里定义URL 。创建一个包含如下内容的新文件,mysite/blog/ urls.py :
它看起来和基本的URLconf 很像。其中的关键是第5 行,注意URL 请求里和根URLconf 匹配的blog/ 已经被去掉了—这样blog 应用程序就变得可以重用了,它不用关心自己是被挂接到blog/ 下,或是news/ 下,还是what/i/had/for/lunch/ 下。第5 行里的正则表达式可以匹配任何URL ,比如/blog/ 。
视图函数archive 是在模式元组第二部分里提供的。(注意我们传递的不是函数的名字,而是一个first-class 的函数对象。当然用字符串也行,你在后面会看到。)
现在来看看效果吧!开发服务器还在运行中么?如果没有,执行manage.py runserver 来启动它,然后在浏览器里输入http://127.0.0.1:8000/blog/ 。你可以看到一个简单朴素的页面,显示了所有你输入的blog 帖子,有标题、发布时间和帖子本身。
2.9 最后的润色
你有好几种方式来继续改进这个基本的blog 引擎。我们来讨论几个关键的概念,把这个项目弄得再漂亮一点。
模板的精确定位
毫不夸张地说,我们的模板实在是太平庸了。毕竟这是一本关于Web 编程而不是Web 设计的书,美术方面的东西你就自己处理吧,但是模板继承是模板系统里另一个能让你减少工作量的特性,特别是当你的页面风格快速成长的时候。
我们这个模板现在是自给自足的。但是如果我们的站点有一个blog 、一个相册和一个链接页面,并且我们希望所有这些都能基于同一个基础风格的话该怎么办?经验告诉我们要是用复制粘贴的办法做出三个几乎完全一样的模板肯定是不行的。在Django 里正确的做法是创建一个基础模板,然后在这之上扩展出其他特定模板来。在mysite/blog/templates 目录里,创建一个叫做base.html 的模板,其内容如下:
虽然不是完全符合XHTML Strict 标准,不过差不多就行了。这里要注意的细节是{% block ... %} 标签。它定义了一个子模板可以修改的命名块(named block )。修改archive.html 模板,让它引用新的基础模板和它的“content ”块,就能在blog app 里使用它了。
这里的{% extends ... %} 标签告诉Django 去查找一个叫base.html 的标签,并将这个模板里命名块的所有内容填入到那个模板里相应的块里去。现在你应该可以看到类似图2.10 的页面了(当然了,你的blog 内容比我这个应该更加精彩)。
图2.10 稍微带点风格的blog
按日期排序
你应该已经注意到blog 的帖子不是按照传统的时间倒序排列的。告诉Django 这么做非常简单。实际上,有好几种做法可供选择。我们可以在model 里加入一个默认的顺序,或者在视图代码里的BlogPost.objects().all() 上添加排序功能。在这里修改model 会比较好,因为基本上帖子都是按时间倒序排列的。如果在model 里设置我们想要的排序方式,Django 里任何访问数据的部分都会采用这个排序结果。
设置model 默认排序的方法是给它定一个Meta 嵌套类,然后设置ordering 属性。
现在看一下blog 的首页(/blog/ )。最新的帖子应该出现在页面最上方了。字符串“-timestamp ”能简洁地通知Django ,“对‘timestamp ’变量按照降序排列”。(如果省略“- ”的话则是按升序排列。)
注意
千万不要忘了小括号里结尾的那个逗号!它代表这是一个单元素的元组,而不是一个带小括号的字符串。Django 在这里要的是一个元组,你可以排序任意数目的变量。如果你在逗号后面加上'title' ,并且你有两个相同发布时间的帖子“A ”和“B ”的话,“A ”就会出现在前面。
通过模板过滤器格式化时间戳
虽然时间戳很好用,但是它的ISO8601 格式却有点怪异。我们现在用Django 模板系统里另一个很酷的特性:过滤器(filter )来把它弄得人性化一点。
由于这是一个表示层的(presentation )细节而非数据结构或是商业逻辑的细节,最适合它的位置应该是模板。打开archive.html 文件并修改“post.timestamp ”一行。
只需像这样用一个竖杠,或“管道”符号接在变量名后面(大括号内部)就能把过滤器应用于变量之上了。刷新blog 首页。现在你可以看到日期的显示更加友好了(“July 7 ”)。
如果date 过滤器的默认风格你也不喜欢的话,你可以传递一个strftime 风格的格式化串作为它的参数。不过这里它用的不是Python 里time 模块的转换代码,而是PHP 的date 函数的格式化说明。例如,如果你要显示星期几,但是不要显示年份的话,代码就变成下面这个样子了:
这个格式化字符串会返回“Friday ,July 6th ”风格的日期。注意这里不要在冒号两边留有空格—Django 的模板引擎对空格敏感。
2.10 总结
我们可以给这个blog 引擎不停地加入新特性(很多人就是这么干的!),但是作为体验Django 能力的话,希望你已经看的差不多了。在构建这个基本的blog app 过程中你已经看到了好几个Django 优雅、简洁的特性:
? 内置的Web 服务器能让开发工作自给自足,同时它能在代码被修改时自动重新加载你的代码。
? 数据模型的创建采用纯Python 方式完成,你不用编写维护任何SQL 代码或XML 描述文件。
? 自动化的admin 应用程序,提供了完整的内容编辑特性,甚至非技术背景的用户也能使用。
? 模板系统,可以用来生成HTML 、CSS 、JavaScript 以及任何文本输出格式。
? 模板过滤器,可以在不解除应用程序商业逻辑的前提下修改表示层的数据显示(比如日期)。
?URLconf 系统,在给予你URL 设计极大灵活性的同时还能将应用程序特定的URL 部分保留在其所属的应用程序内部。
下面是一些可以通过Django 内建的特性就能添加到blog 上的功能,这能让你有个基本的概念:
? 发布关于最新帖子的Atom 或RSS feed (参见第11 章)。
? 添加一个搜索功能,让用户能迅速定位包含特定词句的blog 帖子(参见第8 章里的CMS 例子)。
? 用Django 的“通用视图”来避免在views.py 里编写任何代码(参见第10 章里的Pastebin 示例)。
到此你已经完成了Django 基础的旋风之旅。第3 章会总览Django 中的关键组件和它们背后的设计哲学,以及简要地复述一下Web 开发原则,它们不仅是对Django 自身,而且也是对书后几章的经验教训总结。第4 章将带你进入框架的细节,你会找到很多之前例子中碰到的关于“怎么样,为什么和什么是等等?”问题的解答。第4 章之后你就会对框架有足够的理解,可以继续构建示例应用了:一个内容管理系统(CMS )、一个Pastebin 、一个相册和一个基于Ajax 技术的“live blog ”。
Django 速成:构建一个Blog
Django 自称是“ 最适合开发有限期的完美Web 框架” 。所以现在我们来挑战一下,看看到底能在多短的时间里用Django 完成一个简单的blog 。(关于追求完美的部分我们稍后再细说。)
注意
本章假设你已经安装 了Django 。如果还没有的话,请参见附录B 。
本章所有工作都是在你选择的shell (bash 、tcsh 、zsh 和Cygwin 等)下以命令行方式完成。所以,首先打开终端,键入cd 命令进入你PYTHONPATH 环境变量所指向的目录。在Linux 、Mac OS X 、FreeBSD 等类UNIX 的系统上,你可以用echo $PYTHONPATH 命令来查看其内容。在Win32 的命令行窗口里,则可以输入echo %PYTHONPATH% 。你可以在第1 章里读到更多关于路径和安装的内容。
我们建议你在阅读的同时实际动手构建这个blog 。如果做不到(例如不在电脑旁边,或者没这个耐性)的话,简单读一下本章也是好的。特别是如果你有其他现代Web 框架开发的经验就更好了,因为很多基本的概念都是相似的。
如果你确实打算在电脑上实际操作,同时遇到和你在这里看到的结果不同的情况时,请停下来重新检验刚才的步骤,然后复查之前的两到三步。看看是不是有哪些看上去不重要的地方,或是哪些不能理解的步骤被遗漏掉了。如果还是没发现哪里不对,那就删掉整个项目重新来过。作者在学习 Django 时就用过这个办法;这样比无助地盯着错误信息看上几个小时要有效率,而且重复那些会导致错误的步骤还能帮你加深印象!
2.1 创建项目
组织Django代码 最简单的方式是使用Django 的“ 项目” (project ):一个包含了组成单个网站的所有文件的目录。Django 提供了一个叫django-admin.py 的命令来帮助创建这样项目的目录。在UNIX 上,它的默认安装路径为/usr/bin ;在Win32 上,它的位置是Python 安装目录下的Scripts 文件夹,比如C:/Python25/Scripts 。无论是哪一个平台,你都要保证django-admin.py 在PATH 环境变量里以保证它可以从命令行执行。
为blog 项目创建一个项目目录的django-admin.py 命令是:
在Windows 上,你需要先打开一个DOS 命令窗口。可以通过【开始→ 程序→ 附件→ 命令提示符】来启动这个窗口。另外,你会看到C:/WINDOWS/system32 这样的提示符,而不是$ 符号。
现在我们来看看这个命令为你创建的目录里有点什么内容。在UNIX 上它看起来是这样的:
如果你是在Win32 平台上进行开发,那么打开资源管理 器窗口你可以看到图2.1 这样的文件夹,这里我们所创建的目录是C:/py/django ,项目的内容将会存放在这里。
图2.1 Win32 上的mysite 文件夹
注意
如果你精通Python 的话,你一定知道__init__.py 会把这个项目目录变成一个Python 的包(package )— 相关Python 模块的一个集合。这让我们可以用Python 的“ 点记号” (dot-notation )来指定项目中的某个部分,比如mysite.urls 。(你可以阅读第1 章,以获取更多关于包的内容。)
除了__init__.py ,startproject 命令还创建了以下三个文件:
?manage.py 文件是一个同这个Django 项目一起工作的工具。你可以从它在目录列表中的权限里看到它是可以执行的。我们马上就会用到它。
?settings.py 文件包含了项目的默认设置。包括数据库 信息、调试标志以及其他一些重要的变量。你项目里安装的任何应用都可以访问这个文件。稍后在本章进行过程中我们会展示其更多的作用。
?urls.py 文件在Django 里叫URLconf ,它是一个将URL 模式映射到你应用程序上的配置文件。URLconf 是Django 里非常强大的一个特性。
注意
startproject 命令创建的所有文件都是Python 的源码 文件。这里没有XML 、.ini 文件,或是任何时髦的配置语法。Django 追求的是尽可能地保持“ 纯Python” 这一理念。这让你在拥有诸多灵活性的同时无需在框架里引入任何复杂性。例如,如果你想让你的settings 文件从其他文件导入设置,或是计算一个值而避免硬编码的话都可以畅行无阻— 因为它就是Python 而已。
2.2 运行开发服务器
到这里,你还没有构建完blog 应用,不过Django 为你提供了一些可以就地使用的方便。其中最好用的就是Django 的内置Web 服务器了。这个服务器不是用来部署公共站点,而是用来做快速开发的。其优点在于:
? 不需要安装Apache 、Lighttpd ,或是其他任何实际生产所需的Web 服务器软件 — 如果你在一台新的服务器或是没有服务器环境的开发机上,或是就想实验一下的话,那就非常方便了。
? 它会自动检测到你对Python 源码的修改并且重新加载那些模块。相比每次修改代码后都要手动地重启Web 服务器可是大大地节约了不少时间,这对绝大多数Python 的Web 服务器设置来说都是很有必要的。
? 它知道如何为admin 应用程序寻找并显示静态的媒体文件,所以你就可以直接使用它。
运行开发服务器(或“dev” )简单到只需要一个命令就行了。这里我们要用到项目里的manage.py 工具,这是一个简单的包裹脚本,能直接告诉django-admin.py 去读入项目特定的settings 文件。启动dev 的命令如下:
在Win32 平台你应该能看到类似如下的输出,只是要退出时要按下的快捷键是Ctrl+Break 而不是Ctrl+C 。
在浏览器里输入这个链接,你会看到Django 的“It worked!” 页面,如图2.2 所示。
同时,如果你查看终端的话,你会看到dev 服务器记录下了你的GET 请求。
log 从左到右有4 个部分:时间戳、请求、HTTP 状态码,以及字节数。(你看到的字节数或许会有所不同。)这里状态码404 (“Not Found” )是因为还没有为项目定义任何URL 。而“It worked!” 页面只是Django 委婉地告诉你这一点的方式。
提示
如果服务器不工作的话,请重新检查每一个步骤。不要怕麻烦!有时候直接删掉整个项目,然后重新开始可能反而比劳心劳力地检查每一个文件,每一行代码要来的简单。
如果你成功启动服务器后,我们就可以来设置你的第一个Django 应用了。
2.3 创建Blog 应用
有了项目以后,就可以在它下面创建应用(按Django 的说法是“app” )了。我们再次使用manage.py 来创建这个blog app 。
这样就完成项目的创建了。现在我们在项目的目录下有了一个blog 目录。这是它的内容,首先是UNIX 下的,接着是Windows 资源管理器里的截图(图2.3 )。
图2.3 Win32 上的mysite/blog 文件夹
和项目一样,app 也是一个包。现在models.py 和views.py 里还没有真正的代码,它们只是先占住位子而已。实际上对我们这个简单的blog 来说,我们根本用不着views.py 文件。
要告诉Django 这个app 是项目里的一部分,你需要去编辑settings.py 文件(也称之为“ 配置文件” )。打开配置文件并在文件尾部找到INSTALLED_APPS 元组。把你的app 以模块的形式添加到元组里,就像这样(注意结尾的逗号):
Django 用INSTALLED_APPS 来决定系统里不同部分的配置,包括自动化的admin 应用以及测试 框架。
2.4 设计你的Model
现在我们来到了这个基于Django 的blog 应用的核心部分:models.py 文件。这是我们定义blog 数据结构的地方。根据DRY 原则,Django 会尽量利用你提供给应用程序的model 信息。我们先来创建一个基本的model ,看看Django 用这个信息为我们做了点什么。
用你最习惯的编辑器(如果它能支持Python 模式就最好)打开models.py 文件。你会看到这样的占位文本:
删掉注释,加入下列代码:
这是一个完整的model ,代表了一个有3 个变量的“BlogPost” 对象。(严格说来应该有4 个,Django 会默认为每个model 自动加上一个自增的、唯一的id 变量。)
这个新建的BlogPost 类是django.db.models.Model 的一个子类。这是Django 为数据model 准备的标准基类,它是Django 强大的对象关系映射(ORM )系统的核心。此外,每一个变量都和普通的类属性一样被定义为一个特定变量类(field class )的实例。这些变量类也是在django.db.models 里定义,它们的种类非常多,从BooleanField 到XMLField 应有尽有,可不止这里看到的区区三个。
2.5 设置数据库
如果你没有安装运行数据库服务器的话,我们推荐使用SQLite ,这是最快最简单的办法。它的速度很快,被广泛接受,并且将它的数据库作为一个文件存放在文件系统上。访问控制就是简单的文件权限。更多关于在Django 里使用数据库的信息请参阅附录B 。
如果你安装了数据库服务器(PostgreSQL 、MySQL 、Oracle 和MSSQL ),并且希望用它们而非SQLite 的话,你需要用相应的数据库管理工具为Django 项目创建一个新的数据库。在这里我们把数据库取名为“djangodb” ,不过你可以选用任何喜欢的名字。
当你有了一个(空的)数据库以后,接下来只需要告诉Django 如何使用它即可。这就需要用到项目的settings.py 文件。
使用数据库服务器
很多人都选用PostgreSQL 或MySQL 这样的关系数据库和Django 配合使用。这里有6 个相关的设置(虽然你可能只要两个就够了):DATABASE_ENGINE 、DATABASE_NAME 、DATABASE_HOST 、DATABASE_PORT 、DATABASE_USER 和DATABASE_PASSWORD 。它们的作用从名字上就能看的出来。你只需在相应的位置填入正确的值就可以了。例如,为MySQL 的设定看上去是这样的:
注意
我们没有指定DATABASE_PORT 是因为只有当你的数据库服务器运行在非标准的端口上时才需要那么做。比如,MySQL 服务器的默认端口为3306 。除非你修改了这个设置,否则根本不用指定DATABASE_PORT 。
要知道创建新数据库和(数据库服务器要求的)数据库用户的细节,请参阅附录B 。
使用SQLite
SQLite 非常适合测试,甚至可以部署在没有大量并发写入的情况下。因为SQLite 使用本地文件系统作为存储介质并且用原生的文件系统权限来做访问控制,像主机、端口、用户或密码这种信息一律统统不需要。因此Django 只要知道以下的两个设置就能使用你的SQLite 数据库了。
注意
当SQLite 配合Apache 这样真正的Web 服务器一起使用时,你需要确认拥有Web 服务器进程的账号也拥有对数据库文件以及包含数据库文件目录的写权限。在我们这里用dev 服务器做开发的情况下,这通常不是一个问题,因为运行dev 服务器的用户(你)同时也拥有项目的文件和目录。
SQLite 在Win32 平台上也是非常受欢迎的选择之一,因为它是随同Python 一同免费发布的。假设我们已经为项目(和应用程序)创建了C:/py/django 目录,现在再来创建一个db 目录。
如果你不太熟悉Python 的话,你会注意到这个例子和前一个例子的不同之处,之前我们在sqlite3 上用的是双引号,而在Win32 的版本里,我们用的是单引号。这个和平台是没有关系的—Python 没有字符类型,所以单引号和双引号都是被等同对待。只要保证你用同样的引号引用字符串就可以了!
你还应该注意到了文件夹名字前的小写“r” 。如果你没有读过第一章,那么你应该知道它的意思是这个对象是一个raw 字符串,或者说它把字符串里所有字符都逐字保留下来,不对任何特殊字符组合做转义。例如,/n 通常代表一个新行,但是在raw 字符串里,它(字面上)代表了两个字符:一个反斜杠和一个小写n 。所以raw 字符串的作用(特别是对这里的DOS 文件路径来说)就是告诉Python 不要转义任何特殊字符(如果有的话)。
创建表
现在你可以告诉Django 用你提供的连接信息去连接数据库并且设置应用程序所需的表。命令很简单:
在Django 设置数据库的过程中你会看到类似这样的输出:
当你执行syncdb 命令时,Django 会查找INSTALLED_APPS 里的每一个models.py 文件,并为找到的每一个model 都创建一张数据库表。(在稍后我们碰到华丽的多对多关系时会有例外,但是对这个例子来说是正确的。如果你用的是SQLite ,你还可以看到django.db 数据库会在你指定的位置上被创建出来。)
INSTALLED_APPS 里的其他默认条目也都拥有model 。manage.py syncdb 的输出就确认了这一点,你可以看到Django 为每个app 都创建了一个或多个表。
但这还不是syncdb 命令输出的全部。你还会被问到一些和django.contrib.auth app 有关的问题。
现在你在auth 系统里建立了一个超级用户(就是你自己)。这在等一会我们加入Django 的自动admin 应用时就会变得很方便。
最后,这一过程还包裹了一些和特性fixture 有关的代码,在第4 章里我们在讨论这个。这让你在一个新建立的应用里预先载入数据。我们在这里没有用到这个特性,所以Django 会跳过它。
到此数据库的初始化就完成了,下次你再对这个项目运行syncdb 命令(只要你添加了应用或是model 时就要执行)时,就不会再看到那么多输出了,因为它不需要再次设置表或者提示你创建超级用户了。
2.6 设置自动admin 应用
自动化的后台应用程序admin 称得上是Django“ 皇冠上的明珠” 。任何对为Web 应用创建简单的“CRUD” (Create ,Read ,Update ,Delete )接口感到厌倦的人来说,这绝对是喜从天降。我们会在第11.1 节里深入讨论admin 。现在我们只要拿来直接用就好了。
由于这不是Django 的必要组件,你必须在settings.py 文件里指定你要使用它— 就和指定blog app 一样。打开settings.py 并在INSTALLED_APPS 元组里的'django.contrib.auth' 下面添加这样一行。
每次往项目里添加新的应用后,你都要运行一下syncdb 命令确保它所需的表已经在数据库里创建了。在这里可以看到在INSTALLED_APPS 里添加admin app 并运行syncdb 命令会往我们的数据库里加入一个新的表:
设置完app 以后,我们需要为它指定一个URL 这样才能访问它。你应该已经注意到了在自动生成的urls.py 中的这样两行代码。
删掉第二行开头的# 号(也可以同时删掉第一行)并保存文件。这样你就告诉Django 去加载默认的admin 站点,这是被用于contrib admin 应用程序的一个特殊对象。
最后,你的应用程序需要告诉Django 要在admin 窗口里显示哪一个model 以供编辑。要做到这一点很简单,只要定义之前提到的默认admin 站点,并向其注册BlogPost model 就行了。打开mysite/blog/models.py 文件,确认导入了admin 应用,然后在最后加上一行注册model 的代码。
这里admin 的简单使用只不过是冰山一角,它还可以通过为给定model 建立一个特殊的Admin 类来指定许多不同和admin 有关的选项,然后向那个类注册model 。这个我们稍后再说,在后面的章节里你还会看到admin 的高级使用,特别是本书第三部分和第四部分。
2.7 试用admin
到这里我们已经在Django 里设置了admin app 并向其注册了model ,现在是时候试一试它了。再次运行manage.py runserver 命令,随后在浏览器里输入http://127.0.0.1:8000/admin/ 。(你的dev 服务器可能不是这个地址,没关系,只要在后面加上admin/ 就可以了。)你应该会看到一个登录窗口,如图2.4 所示。
输入你之前创建的“ 超级用户” 名字和密码。登录后,你就可以看到admin 的主页了,如图2.5 所示。
图2.4 admin 登录页面
图2.5 admin 主页
本书稍后会解释这个界面,现在只需确认你的应用程序Blog 和截图里一样出现在屏幕上。如果没有的话,请重新检查之前的步骤。
提示
三个最常见的“ 我的app 没有显示在admin 里” 的原因是:
1 )忘记向admin.site.register 注册你的model 类;
2 )models.py 里有错误;以及
3 )忘记在settings.py 中的INSTALLED_ APPS 里添加app 。
有没有内容的blog 么?点击Blog Posts 右侧的Add 按钮。Admin 会显示一个表单让你添加新的帖子,如图2.6 所示。
图2.6 通过admin 添加新内容
给你的帖子取一个名字然后往里填一点内容。至于时间戳,你可以点击Today 和Now 的快捷链接来获取当前的日期和时间。你还可以点击日历或时钟标志来方便地选择时间。
完成之后,点击Save 按钮保存。你会收到一条确认消息(“The blog post 'BlogPost object' was added successfully.” )和一个列出你所有blog 帖子的列表— 目前只有一篇而已,如图2.7 所示。
为什么帖子有“BlogPost object” 这么难看的名字?Django 的设计是希望能灵活地处理任意类型的内容,所以它不会去猜测对于给定的内容什么变量才是最合适的。在第三部分的例子里,你会看到如何为你对象的默认标签指定一个特定变量,或是特别定制的字符串。
现在点击右上角的“Add Blog Post +” 按钮添加另一篇不同内容的帖子。当你回到列表视图里,你会看见页面上加入了另一个BlogPost 。如果你刷新页面或是离开应用程序再回来的话,输出不会有任何变化— 你一定不会喜欢看见所有条目都被标为“BlogPost object” ,如图2.8 所示。你不是第一个这么想的人,“ 总有办法让它看起来顺眼一点吧。”
图2.7 成功地保存你的第一篇blog
图2.8 不太有用的小节页面
不过我们不需要等到那个时候来清理admin 视图里的这些列表显示。之前我们通过很少的配置就激活了admin 工具,即向admin app 注册我们的model 。现在只要额外的两行代码以及对注册调用的一些修改,我们就可以让列表看起来更漂亮更有用。更新你的mysite/blog/models. py 文件,添加一个BlogPostAdmin 类,并将它加到注册代码那一行里,于是现在models.py 看起来是这样的:
开发服务器会注意到你的修改并自动重新加载model 文件。如果你去看一下命令行shell ,就能看到这些效果。
刷新一下页面,现在你就可以看到一个更有意义的页面了,它是根据你添加到BlogPostAdmin 类里list_display 变量来生成输出的,如图2.9 所示。
图2.9 这样就好多了
试着点一下Title 和Timestamp 列的标题— 每一个都影响了你的条目是如何排序的。例如,点一下Title 会按照升序排列标题,再点一下则变成降序排列。
Admin 还有很多有用的特性只需一到两行代码就可以激活:搜索、自定义排序、过滤等。就像我们多次提到的,在本书第三和第四部分里会更详细地展示这些内容。
2.8 建立Blog 的公共部分
完成我们应用的数据库部分和admin 部分后,现在来看看面向公众的页面部分。从Django 的角度来说,一个页面具有三个典型的组件:
? 一个模板(template ),模板负责将传递进来的信息显示出来(用一种类似Python 字典的对象Context )。
? 一个视图(view )函数,它负责获取要显示的信息,通常都是从数据库里取得。
? 一个URL 模式,它用来把收到的请求和你的视图函数匹配,有时也会向视图传递一些参数。
创建模板
Django 的模板语言相当简单,我们直接来看代码。这是一个简单的显示单个blog 帖子的模板:
它就是一个HTML (虽然Django 模板可以用于任何形式的输出)加上一些大括号里的特殊模板标签。这些是变量标签(variable tag ),用于显示传递给模板的数据。在变量标签里,你可以用Python 风格的dotted-notation (点记号)来访问传递给模板的对象的属性。例如,这里假设你传递了一个叫“post” 的BlogPost 对象。这三行模板代码分别从BlogPost 对象的title 、timestamp 和body 变量里获取了相应的值。
现在我们稍微改进一下这个模板,通过Django 的for 模板标签让它能显示多篇blog 帖子。
原来的3 行没有动,我们只是简单地增加了一个叫做for 的块标签(block tag ),用它将模板渲染到序列中的每个元素上。其语法和Python 的循环语法是一致的。注意和变量标签不同,块标签是包含在{% ... %} 里的。
把这5 行模板代码保存到文件archive.html 里,然后把文件放到你的blog app 目录里的templates 目录下。这个文件的路径即为:
模板本身的名字是随意取的(叫它foo.html 也没问题),但是templates 目录的名字则是强制的。Django 在默认情况下会在搜索模板时逐个查看你安装的应用程序下的每一个templates 目录。
创建一个视图函数
现在我们来编写一个从数据库读取所有blog 帖子的视图函数,并用我们的模板将它们显示出来。打开blog/views.py 文件并输入:
先略过import 那几行(它们载入了我们需要的函数和类),我们来逐行解释一下这个视图函数。
第5 行:每个Django 视图函数都将django.http.HttpRequest 对象作为它的第一个参数。它还可以通过URLconf 接受其他参数,你将来会大量用到这个特性。
第6 行:当我们把BlogPost 类作为django.db.model.Model 的一个子类时,我们就获得了Django 对象关系映射的全部力量。这一行只是使用ORM (对象关系映射,详见第3 章和第4 章)的一件简单例子,获取数据库里所有BlogPost 对象。
第7 行:这里我们只需告诉Django 模板的名字就能创建模板对象t 。因为我们把它保存在app 下的templates 目录里,Django 无需更多指示就能找到它。
第8 行:Django 模板渲染的数据是由一个字典类的对象context 提供的,这里的context c 只有一对键和值。
第9 行:每个Django 视图函数都会返回一个django.http.HttpResponse 对象。最简单的就是给其构造函数传递一个字符串。这里模板的render 方法返回的正是一个字符串。
创建一个URL 模式
我们的页面还差一步就可以工作了— 和任何网页一样,它还需要一个URL 。
当然我们可以直接在mysite/urls.py 里创建所需的URL 模式,但是那样做只会在项目和app 之间制造混乱的耦合。Blog app 还可以用在别的地方,所以最好是它能为自己的URL 负责。这需要两个简单的步骤。
第一步和激活admin 很相似。在mysite/urls.py 里有一行被注释的示例几乎就是我们需要的代码。把它改成这样:
这会捕捉任何以blog/ 开始的请求,并把它们传递给一个你马上要新建的URLconf 。
第二步是在blog 应用程序包里定义URL 。创建一个包含如下内容的新文件,mysite/blog/ urls.py :
它看起来和基本的URLconf 很像。其中的关键是第5 行,注意URL 请求里和根URLconf 匹配的blog/ 已经被去掉了— 这样blog 应用程序就变得可以重用了,它不用关心自己是被挂接到blog/ 下,或是news/ 下,还是what/i/had/for/lunch/ 下。第5 行里的正则表达式可以匹配任何URL ,比如/blog/ 。
视图函数archive 是在模式元组第二部分里提供的。(注意我们传递的不是函数的名字,而是一个first-class 的函数对象。当然用字符串也行,你在后面会看到。)
现在来看看效果吧!开发服务器还在运行中么?如果没有,执行manage.py runserver 来启动它,然后在浏览器里输入http://127.0.0.1:8000/blog/ 。你可以看到一个简单朴素的页面,显示了所有你输入的blog 帖子,有标题、发布时间和帖子本身。
2.9 最后的润色
你有好几种方式来继续改进这个基本的blog 引擎。我们来讨论几个关键的概念,把这个项目弄得再漂亮一点。
模板的精确定位
毫不夸张地说,我们的模板实在是太平庸了。毕竟这是一本关于Web编程 而不是Web 设计的书,美术方面的东西你就自己处理吧,但是模板继承是模板系统里另一个能让你减少工作量的特性,特别是当你的页面风格快速成长的时候。
我们这个模板现在是自给自足的。但是如果我们的站点有一个blog 、一个相册和一个链接页面,并且我们希望所有这些都能基于同一个基础风格的话该怎么办?经验告诉我们要是用复制粘贴的办法做出三个几乎完全一样的模板肯定是不行的。在Django 里正确的做法是创建一个基础模板,然后在这之上扩展出其他特定模板来。在mysite/blog/templates 目录里,创建一个叫做base.html 的模板,其内容如下:
虽然不是完全符合XHTML Strict 标准,不过差不多就行了。这里要注意的细节是{% block ... %} 标签。它定义了一个子模板可以修改的命名块(named block )。修改archive.html 模板,让它引用新的基础模板和它的“content” 块,就能在blog app 里使用它了。
这里的{% extends ... %} 标签告诉Django 去查找一个叫base.html 的标签,并将这个模板里命名块的所有内容填入到那个模板里相应的块里去。现在你应该可以看到类似图2.10 的页面了(当然了,你的blog 内容比我这个应该更加精彩)。
图2.10 稍微带点风格的blog
按日期排序
你应该已经注意到blog 的帖子不是按照传统的时间倒序排列的。告诉Django 这么做非常简单。实际上,有好几种做法可供选择。我们可以在model 里加入一个默认的顺序,或者在视图代码里的BlogPost.objects().all() 上添加排序功能。在这里修改model 会比较好,因为基本上帖子都是按时间倒序排列的。如果在model 里设置我们想要的排序方式,Django 里任何访问数据的部分都会采用这个排序结果。
设置model 默认排序的方法是给它定一个Meta 嵌套类,然后设置ordering 属性。
现在看一下blog 的首页(/blog/ )。最新的帖子应该出现在页面最上方了。字符串“-timestamp” 能简洁地通知Django ,“ 对‘timestamp’ 变量按照降序排列” 。(如果省略“-” 的话则是按升序排列。)
注意
千万不要忘了小括号里结尾的那个逗号!它代表这是一个单元素的元组,而不是一个带小括号的字符串。Django 在这里要的是一个元组,你可以排序任意数目的变量。如果你在逗号后面加上'title' ,并且你有两个相同发布时间的帖子“A” 和“B” 的话,“A” 就会出现在前面。
通过模板过滤器格式化时间戳
虽然时间戳很好用,但是它的ISO8601 格式却有点怪异。我们现在用Django 模板系统里另一个很酷的特性:过滤器(filter )来把它弄得人性化一点。
由于这是一个表示层的(presentation )细节而非数据结构或是商业逻辑的细节,最适合它的位置应该是模板。打开archive.html 文件并修改“post.timestamp” 一行。
只需像这样用一个竖杠,或“ 管道” 符号接在变量名后面(大括号内部)就能把过滤器应用于变量之上了。刷新blog 首页。现在你可以看到日期的显示更加友好了(“July 7” )。
如果date 过滤器的默认风格你也不喜欢的话,你可以传递一个strftime 风格的格式化串作为它的参数。不过这里它用的不是Python 里time 模块的转换代码,而是PHP 的date 函数的格式化说明。例如,如果你要显示星期几,但是不要显示年份的话,代码就变成下面这个样子了:
这个格式化字符串会返回“Friday ,July 6th” 风格的日期。注意这里不要在冒号两边留有空格—Django 的模板引擎对空格敏感。
2.10 总结
我们可以给这个blog 引擎不停地加入新特性(很多人就是这么干的!),但是作为体验Django 能力的话,希望你已经看的差不多了。在构建这个基本的blog app 过程中你已经看到了好几个Django 优雅、简洁的特性:
? 内置的Web 服务器能让开发工作自给自足,同时它能在代码被修改时自动重新加载你的代码。
? 数据模型的创建采用纯Python 方式完成,你不用编写维护任何SQL 代码或XML 描述文件。
? 自动化的admin 应用程序,提供了完整的内容编辑特性,甚至非技术 背景的用户也能使用。
? 模板系统,可以用来生成HTML 、CSS 、JavaScript 以及任何文本输出格式。
? 模板过滤器,可以在不解除应用程序商业逻辑的前提下修改表示层的数据显示(比如日期)。
?URLconf 系统,在给予你URL 设计极大灵活性的同时还能将应用程序特定的URL 部分保留在其所属的应用程序内部。
下面是一些可以通过Django 内建的特性就能添加到blog 上的功能,这能
让你有个基本的概念:
? 发布关于最新帖子的Atom 或RSS feed (参见第11 章)。
? 添加一个搜索功能,让用户能迅速定位包含特定词句的blog 帖子(参见第8 章里的CMS 例子)。
? 用Django 的“ 通用视图” 来避免在views.py 里编写任何代码(参见第10 章里的Pastebin 示例)。
到此你已经完成了Django 基础的旋风之旅。第3 章会总览Django 中的关键组件和它们背后的设计哲学,以及简要地复述一下Web 开发原则,它们不仅是对Django 自身,而且也是对书后几章的经验教训总结。第4 章将带你进入框架的细节,你会找到很多之前例子中碰到的关于“ 怎么样,为什么和什么是等等?” 问题的解答。第4 章之后你就会对框架有足够的理解,可以继续构建示例应用了