使用 Django 和 MongoDB 构建博客
本文展示了如何使用 MongoDB 文档数据库和 Django Web 框架创建一个简单的博客网站。
MongoDB 基础知识MongoDB 是一个开源的、面向文档的数据库,而非传统的关联数据库,由 Dwight Merriman 和 Eliot Horowitz 使用 C++ 编写。面向文档的数据库并非指存储 Microsoft Word 文档,而是指存储半结构化数据。您可以将任意二进制 JSON 对象 (BSON) 输入到 MongoDB 数据库中。它可以在 UNIX 机器和 Windows 上运行,并支持复制和分片。
您的 Linux 发行版可能包含 MongoDB 软件包,因此如果尚未安装,请继续安装。或者,您可以从 http://www.mongodb.org 下载预编译的二进制文件或获取 MongoDB 源代码并自行编译。
在 Debian 7 系统上,您可以使用以下命令安装 MongoDB
# apt-get install mongodb
安装 MongoDB 后,使用以下命令启动 MongoDB 服务器进程
# service mongodb start
同样,您可以使用以下命令停止正在运行的 MongoDB 服务器
# service mongodb stop
安装后,在您的 UNIX shell 中输入 mongo --version
以查找您正在使用的 MongoDB 版本,并输入 mongo
进入 MongoDB shell 并检查 MongoDB 服务器进程是否正在运行。
默认情况下,MongoDB 服务器进程监听 localhost 的 27017 端口。您可以根据需要更改它,但如果 MongoDB 服务器和 Django 安装在同一台机器上,则保持原样更安全。
MongoDB 的配置文件是 /etc/mongodb.conf。但是,如果您想在同一台 UNIX 机器上运行多个 MongoDB 服务器,您可以绕过 /etc/mongodb.conf 文件,并使用命令行选项,这些选项允许您使用不同的端口号、不同的 IP 甚至不同的 MongoDB 配置文件。

图 1. MongoDB 术语
图 1 展示了最有用的 MongoDB 术语及其对应的 SQL 术语。
在没有参数且没有 root 权限的情况下,在 Linux 机器上启动 MongoDB 服务器进程 (mongod) 应该生成类似于以下的输出
$ mongod
mongod --help for help and startup options
Fri Sep 27 23:21:33 [initandlisten] MongoDB starting :
↪pid=7991 port=27017 dbpath=/data/db/ 64-bit host=mail
Fri Sep 27 23:21:33 [initandlisten] db version v2.0.6,
↪pdfile version 4.5
Fri Sep 27 23:21:33 [initandlisten] git version: nogitversion
Fri Sep 27 23:21:33 [initandlisten] build info: Linux z6
↪3.8-trunk-amd64 #1 SMP Debian 3.8.3-1~experimental.1
↪x86_64 BOOST_LIB_VERSION=1_49
Fri Sep 27 23:21:33 [initandlisten] options: {}
Fri Sep 27 23:21:33 [initandlisten] exception in initAndListen:
↪10296 dbpath (/data/db/) does not exist, terminating
Fri Sep 27 23:21:33 dbexit:
Fri Sep 27 23:21:33 [initandlisten] shutdown: going to close
↪listening sockets...
Fri Sep 27 23:21:33 [initandlisten] shutdown: going to
↪flush diaglog...
Fri Sep 27 23:21:33 [initandlisten] shutdown: going to
↪close sockets...
Fri Sep 27 23:21:33 [initandlisten] shutdown: waiting
↪for fs preallocator...
Fri Sep 27 23:21:33 [initandlisten] shutdown: lock for
↪final commit...
Fri Sep 27 23:21:33 [initandlisten] shutdown: final commit...
Fri Sep 27 23:21:33 [initandlisten] shutdown: closing all files...
Fri Sep 27 23:21:33 [initandlisten] closeAllFiles() finished
Fri Sep 27 23:21:33 dbexit: really exiting now
Django 基础知识
Django 是一个高级 Python Web 框架,它鼓励快速开发和简洁、实用的设计。它允许您快速构建 Web 应用程序。Instagram、Mozilla 和 Pinterest 都使用 Django。
简单来说,Django 是一个用 Python 编写的库集合。为了使用 Django 创建网站,您基本上需要编写使用 Django 库的 Python 代码。如果您已经对 Python 有了良好的工作知识,您只需要了解 Django 库的工作原理即可。
Django 遵循 MVC(模型-视图-控制器)设计模式的一个略微修改的版本,称为 MTV(模型-模板-视图)。MTV 通过核心处理控制器的工作,所有其他工作都在模型、模板和视图中完成。根据 Django 的理念,真正重要的不是术语,而是完成工作。
在 Debian 7 系统上,您可以使用以下命令安装 Django
# apt-get install python-django
为了确保一切按预期工作,请输入以下 Django 命令,该命令将打印 Django 的版本
# django-admin version
1.5.1
Python 和 Django 如何与 MongoDB 通信
您需要一个名为 PyMongo 的 Python 模块,才能从 Python 与 MongoDB 通信。在 Debian 7 系统上,您可以按如下方式安装它
# apt-get install python-pymongo
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following extra packages will be installed:
python-bson python-bson-ext python-gridfs python-pymongo-ext
The following NEW packages will be installed:
python-bson python-bson-ext python-gridfs python-pymongo
↪python-pymongo-ext
0 upgraded, 5 newly installed, 0 to remove and 0 not upgraded.
Need to get 212 kB of archives.
After this operation, 928 kB of additional disk space will be used.
Do you want to continue [Y/n]?
以下示例 Python 代码(保存为 connect.py)连接到 MongoDB 数据库,打印 mongodb://127.0.0.1:27017 服务器的可用数据库,并关闭 MongoDB 连接
import pymongo
# Open the MongoDB connection
connMongo = pymongo.Connection('mongodb://127.0.0.1:27017')
# Print the available MongoDB databases
print connMongo.database_names()
# Close the MongoDB connection
connMongo.close()
您可以按如下方式运行这个小的 Python 脚本
$ python connect.py
[u'LJ', u'local', u'test']
输出显示,在运行脚本时,存在三个数据库,分别名为 LJ、local 和 test。虽然在本文的其余部分不会直接使用 PyMongo,但了解它对于测试和故障排除很有用。
一般来说,Django 为它支持的每个关系数据库都有一个包装器,但 MongoDB 是一个非关系数据库,因此您需要一些外部帮助。您需要 MongoEngine Python 包才能使用 MongoDB。其他选项包括 Ming、MongoKit、django-mongodb 和 django-nonrel。在我看来,MongoEngine 是最佳选择。
MongoEngine 是一个为 MongoDB 创建的对象-文档映射器,遵循 Django 的 ORM 风格。您可以通过执行以下命令来安装它
# apt-get install python-mongoengine
MongoEngine 基于 PyMongo,这就是为什么您需要了解一些关于 PyMongo 的基本知识。
对于那些熟悉 Django 的人来说,您应该知道,当您使用 MongoEngine 时,您将失去 Django Admin 面板和 python manage.py syncdb
命令。失去 Django Admin 面板是一个主要的缺点,但 MongoDB 提供了关系数据库无法提供的功能。
假设您注册了一个新域名来托管您的个人网站。该网站还将有一个博客。您不想使用 CMS(例如 Joomla! 或 WordPress)来创建博客,而是希望对网站有更多的控制权,因此您决定使用 Django 创建博客,并使用 MongoDB 存储博客数据。
这个解决方案的好处是,如果您已经熟悉 Django,那么开发、测试和交付完整版本的博客网站不会超过两个小时。
注意:这里提出的解决方案力求尽可能地接近 Django 原生方式。与通常的 Django 方式唯一不同的是使用了 MongoDB。
解决方案如果您尝试访问一个尚不存在的 MongoDB,MongoDB 将会创建它。如果您尝试写入一个不存在的 MongoDB 集合(表),也会发生同样的情况。因此,您无需在 MongoDB 上执行任何命令,但您应该非常小心,不要在代码中出现任何拼写错误。
在 Django 上执行以下步骤。
1) 创建一个名为 LJ 的新项目
$ django-admin.py startproject LJ
$ cd LJ
manage.py 脚本是为每个 Django 项目创建的,并且是 django-admin.py 的包装器。您无需对其进行任何更改。
2) 运行测试开发 Web 服务器,以查看一切是否正常
$ python manage.py runserver
通过访问 https://127.0.0.1:8000/ (或 http://127.0.0.1:8000/),您将看到图 2。

图 2. 测试开发 Web 服务器
每次您更改 Django 项目时,测试开发服务器都会自动重启。
3) 为博客创建一个名为 LJblog 的应用程序
$ python manage.py startapp LJblog
4) 将 LJblog 应用程序的名称添加到 LJ/settings.py 文件中 INSTALLED_APPS
列表中。如果您不安装该应用程序,您将无法使用它。因此,INSTALLED_APPS
变量应具有以下值
INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.messages',
'django.contrib.staticfiles',
'LJblog',
'django_extensions',
)
正如您在此处看到的,还需要安装一个名为 django-extensions 的软件包。如果您的 Linux 发行版未提供现成的安装包,请访问 Django Extensions 网站以获取有关安装它的说明。
4) LJ/settings.py 文件中还需要进行许多其他更改。以下 diff
输出显示了这些更改
$ diff settings.py{,.orig}
3,5d2
< import os
< PROJECT_ROOT = os.path.abspath(os.path.dirname(__file__))
<
14a12,23
> DATABASES = {
> 'default': {
> 'ENGINE': 'django.db.backends.', # Add 'postgresql_psycopg2',
# 'mysql', 'sqlite3' or
# 'oracle'.
> 'NAME': '', # Or path to database file if using sqlite3.
> # The following settings are not used with sqlite3:
> 'USER': '',
> 'PASSWORD': '',
> 'HOST': '', # Empty for localhost through domain sockets
# or '127.0.0.1' for localhost through TCP.
> 'PORT': '', # Set to empty string for default.
> }
> }
>
44c53
< MEDIA_ROOT = os.path.join(PROJECT_ROOT, '..', 'media')
---
> MEDIA_ROOT = ''
49c58
< MEDIA_URL = '/media/'
---
> MEDIA_URL = ''
55c64
< STATIC_ROOT = os.path.join(PROJECT_ROOT, '..', 'static')
---
> STATIC_ROOT = ''
63d71
< os.path.join(PROJECT_ROOT, 'static'),
103d110
< os.path.join(PROJECT_ROOT, 'templates'),
148,159d154
<
< AUTHENTICATION_BACKENDS = (
< 'mongoengine.django.auth.MongoEngineBackend',
< )
<
< SESSION_ENGINE = 'mongoengine.django.sessions'
<
< MONGO_DATABASE_NAME = 'LJ_blog'
<
< from mongoengine import connect
< connect(MONGO_DATABASE_NAME)
<
注意:MongoDB 数据库的名称在 MONGO_DATABASE_NAME
变量中定义和存储。
5) LJ/urls.py 文件的内容应如下所示
from django.conf.urls import patterns, include, url
from django.conf import settings
from LJblog.views import PostListView
urlpatterns = patterns('',
url(r'^$', PostListView.as_view(), name='list'),
url(r'^post/', include('LJblog.urls'))
)
6) 在 LJ/LJ 目录中,您需要创建两个目录,分别名为 static 和 templates,并将一些文件和目录复制到其中。该项目使用 Twitter Bootstrap 工具集来创建网站和 Web 应用程序。
以下输出显示了 static 目录的完整内容
$ ls -lR static/
total 0
drwxr-xr-x@ 6 mtsouk staff 204 Sep 21 14:13 bootstrap
static//bootstrap:
total 0
drwxr-xr-x@ 6 mtsouk staff 204 Jan 5 2013 css
drwxr-xr-x@ 4 mtsouk staff 136 Jan 5 2013 img
drwxr-xr-x@ 4 mtsouk staff 136 Jan 5 2013 js
static//bootstrap/css:
total 544
-rwxr-xr-x@ 1 mtsouk staff 21751 Jan 5 2013
↪bootstrap-responsive.css
-rwxr-xr-x@ 1 mtsouk staff 16553 Jan 5 2013
↪bootstrap-responsive.min.css
-rwxr-xr-x@ 1 mtsouk staff 124223 Jan 5 2013
↪bootstrap.css
-rwxr-xr-x@ 1 mtsouk staff 103314 Jan 5 2013
↪bootstrap.min.css
static//bootstrap/img:
total 56
-rwxr-xr-x@ 1 mtsouk staff 8777 Jan 5 2013
↪glyphicons-halflings-white.png
-rwxr-xr-x@ 1 mtsouk staff 12799 Jan 5 2013
↪glyphicons-halflings.png
static//bootstrap/js:
total 184
-rwxr-xr-x@ 1 mtsouk staff 58516 Jan 5 2013
↪bootstrap.js
-rwxr-xr-x@ 1 mtsouk staff 31596 Jan 5 2013
↪bootstrap.min.js
templates 目录包含两个文件,如 ls -lR
命令的输出所示
$ ls -lR templates/
total 16
-rwxr-xr-x@ 1 mtsouk staff 1389 Sep 25 21:25 base.html
-rwxr-xr-x@ 1 mtsouk staff 148 Jan 5 2013 messages.html
base.html 文件包含您可以更改的项目特定信息。
7) 与 MongoDB 数据库的连接发生在 LJ/settings.py 文件中。Django 需要以下两个命令来连接到 MongoDB 数据库
from mongoengine import connect
connect(MONGO_DATABASE_NAME)
8) 接下来,您应该在 templates/LJblog 目录中创建四个 HTML 文件。它们是用于创建、读取、更新和删除操作的显示 Web 页面
-
create.html
-
detail.html
-
list.html
-
update.html
选择的文件名必须与 LJblog/urls.py 文件中找到的参数匹配。
9) LJblog/urls.py 文件的内容如下所示
from django.conf.urls import patterns, url
from views import PostCreateView, PostDetailView,
↪PostUpdateView, PostDeleteView
urlpatterns = patterns('',
url(r'^add/$', PostCreateView.as_view(), name='create'),
url(r'^(?P<pk>[\w\d]+)/$', PostDetailView.as_view(),
↪name='detail'),
url(r'^(?P<pk>[\w\d]+)/edit/$', PostUpdateView.as_view(),
↪name='update'),
url(r'^(?P<pk>[\w\d]+)/delete/$', PostDeleteView.as_view(),
↪name='delete'),
)
10) 接下来,您需要编辑 models.py 文件。此文件是定义数据模型的地方。
使用 Django 的 ORM(对象-关系映射器)是该项目的目标之一。ORM 允许在 models.py 中定义的 Python 类访问选定的数据库,而无需您直接处理数据库。ORM 是 Django 的一个主要优势。
Post Python 类定义如下
class Post(Document):
user = ReferenceField(User, reverse_delete_rule=CASCADE)
title = StringField(max_length=200, required=True)
text = StringField(required=True)
text_length = IntField()
date_modified = DateTimeField(default=datetime.now)
is_published = BooleanField()
正如您将在本文后面看到的那样,Post MongoDB 表与 Post Python 类直接关联。
11) 然后,您需要编辑 forms.py 文件。forms.py 文件允许 Django 访问用户提交的表单数据。
12) 最后但并非最不重要的一点是,您应该编辑 views.py 文件。此文件包含处理数据以及各种其他功能的函数。
项目目录结构以及包含的文件如图 3 所示。您还会注意到以 .pyc 结尾的文件。这些是由 Python 解释器创建的字节码文件,由 Python 虚拟机执行。

图 3. Django 项目的目录结构
您可以使用 MongoDB 命令检查 LJ_blog 集合的内容
> use LJ_blog;
switched to db LJ_blog
> show collections;
post
system.indexes
> db.post.find();
{ "_id" : ObjectId("523d83de8491973b242e2772"), "title" :
↪"First blog entry!", "text" : "This is my first
↪blog entry.\r\Mihalis Tsoukalos", "text_length" : 47,
↪"date_modified" : ISODate("2013-09-21T06:32:46.289Z"),
↪"is_published" : true }
{ "_id" : ObjectId("523d83f88491973b242e2773"), "title" :
↪"Another post", "text" : "Just another blog post!",
↪"text_length" : 23, "date_modified" :
↪ISODate("2013-09-21T06:33:12.321Z"), "is_published" : true }
{ "_id" : ObjectId("523d86f58491973b9e3c8c78"), "title" :
↪"Just another test!", "text" : "Just another test!\r\nLJ",
↪"text_length" : 22, "date_modified" :
↪ISODate("2013-09-21T06:45:57.092Z"), "is_published" : true }
>
注意:每次您在 MongoDB 中插入 BSON 文档时,MongoDB 都会自动生成一个名为 _id 的新字段。_id 字段充当主键,并且始终为 12 个字节长。
现在,您应该通过运行测试开发服务器并尝试连接到 https://127.0.0.1:8000/ 来检查一切是否正常
$ python manage.py runserver
Validating models...
0 errors found
September 21, 2013 - 07:25:07
Django version 1.5.1, using settings 'LJ.settings'
Development server is running at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
如果一切正常,访问 http://127.0.0.1:8000/ 或 https://127.0.0.1:8000/ 后,您将看到类似于图 4 的内容。

图 4. LJblog 应用程序已启动并运行。
在使用该应用程序时,测试开发服务器的输出会更新,并且看起来类似于以下内容
[25/Sep/2013 12:18:28] "GET / HTTP/1.1" 200 1320
[25/Sep/2013 12:18:28] "GET /static/bootstrap/css/bootstrap.min.css
↪HTTP/1.1" 200 103314
[25/Sep/2013 12:18:28] "GET /static/bootstrap/js/bootstrap.min.js
↪HTTP/1.1" 200 31596
[25/Sep/2013 12:18:28] "GET /static/bootstrap/css/
↪bootstrap-responsive.css HTTP/1.1" 200 21751
[25/Sep/2013 12:18:32] "GET / HTTP/1.1" 200 1320
[25/Sep/2013 12:18:33] "GET /post/add/ HTTP/1.1" 200 1823
[25/Sep/2013 12:18:34] "GET /?all_posts HTTP/1.1" 200 1320
[25/Sep/2013 16:01:10] "GET /post/5243295f8491976bd8f016d0/edit/
↪HTTP/1.1" 200 1841
[25/Sep/2013 16:01:18] "GET /post/5243295f8491976bd8f016d0/delete/
↪HTTP/1.1" 302 0
该输出对于调试目的很有用,尤其是在您没有在 Web 浏览器上获得预期结果时。
如果您想删除 LJ_blog 集合以便从头开始创建您的博客,请谨慎使用以下命令
> db.post.drop()
true
> db.post.find();
> show collections;
system.indexes
将 Django 网站部署到生产服务器
解释完整的部署过程超出了本文的范围,但我想提供一些有用的提示。当您尝试在生产服务器上运行您的 Django 项目时,请记住以下事项
1) 关闭 LJ/settings.py 中的调试模式
DEBUG = False
TEMPLATE_DEBUG = DEBUG
2) 将 LJ/settings.py 中的 ADMINS 设置更改为有用的内容
ADMINS = (
('Mihalis', 'someEmail@Domain.GR'),
)
3) 安装并激活 mod_python Apache 模块。
4) 您可以使用 mod_wsgi 代替 mod_python。
为什么使用 MongoDB 而不是关系数据库?您可能想知道为什么应该使用 NoSQL 数据库(例如 MongoDB)而不是传统的 DBMS(如 MySQL 或 PostgreSQL)。虽然可以使用关系数据库,但以下是更喜欢 MongoDB 的原因
-
MongoDB 通常更快。
-
MongoDB 更适合高流量网站。
-
MongoDB 支持分片。分片(也称为水平分区)是将单个数据库分布在机器集群中的过程。
-
MongoDB 支持复制。
-
使用 MongoDB 时,您的数据模式可能会在不停机的情况下更改。
-
根据应用程序的不同,在面向文档的数据库中开发可能会感觉更自然。
-
MongoDB 具有一个易于使用的协议,用于存储大型文件和文件元数据,称为 GridFS。
正如我在此处解释的那样,MongoDB 和 Django 确实可以协同工作。但是,要 100% Django 原生,还缺少两件事:对 Django Admin 面板的支持和对 syncdb
命令的支持。
本文“资源”部分列出了此项目完整代码的链接。
致谢我要感谢 Josh Ourisman 回答了我在撰写本文时遇到的一些问题。
资源本文代码:http://www.mtsoukalos.eu/FILES/MongoDjango.zip
MongoDB:http://www.mongodb.org
Pymongo:http://api.mongodb.org/python/current
BSON:http://bsonspec.org
Django 网页:https://django.ac.cn
MongoEngine:http://mongoengine.org
Django Extensions:https://django-extensions.readthedocs.org/en/latest
Django Book:http://www.djangobook.com/en/2.0/index.html
Twitter Bootstrap:http://en.wikipedia.org/wiki/Twitter_Bootstrap
MongoKit:http://namlook.github.io/mongokit
mod_wsgi:http://code.google.com/p/modwsgi