使用 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

Mihalis Tsoukalos 是一位 UNIX 管理员和开发人员,一位 DBA 和数学家,他喜欢技术写作。他是 Go Systems ProgrammingMastering Go 的作者。您可以通过 http://www.mtsoukalos.eu 和 @mactsouk 联系到他。

加载 Disqus 评论