============================
Django Step by Step (六)
============================
:作者: limodou
:联系: limodou@gmail.com
:版本: 0.1
:主页: http://wiki.woodpecker.org.cn/moin/NewEdit
:BLOG: http://www.donews.net/limodou
:版权: FDL
.. contents:: 目录
.. sectnum::
引言
=====
以后的例子可能会越来越复杂,没办法因为我们用的东西越来越复杂,同时我们的能力也在增长。
下面我们按照 TurboGears_ 的 `20 Minute Wiki Tutorial`_ 的例子仿照一个,我们要用 Django_ 来做 wiki。我不会按 TurboGears 的操作去做,只是实现一个我认为的最简单的 wiki。
.. _TurboGears: http://www.turbogears.org/
.. _`20 Minute Wiki Tutorial`: http://www.turbogears.org/docs/wiki20/
.. _Django: http://www.djangoproject.com/
现在我的要求是:
做一个简单的wiki,要可以修改当前页面,即在页面下面提供一个编辑的按钮。然后还要识别页面中的两个开头大写的单词为页面切换点,可以进入一个已经生成好的页面,或提示创建一个新页面。
下面我们将开始创建 Django 中的 app 了。
先说一下。如果你看过官方版的教程,它就是讲述了一个 Poll 的 app 的生成过程。那么一个 app 就是一个功能的集合,它有自已的 model ,view 和相应的模板,还可以带自已的 urls.py 。那么它也是一个独立的目录,这样一个 app 就可以独立地进行安装,你可以把它安装到其它的 Django 服务器中去。因此采用 app 的组织形式非常有意义。而且 ``adango-admin.py`` 也提供了一个针对 app 的命令,一会我们就会看到。而且 Django 提供一些自动功能也完全是针对于 app 这种结构的。Model, Template, View 就合成了 MTV 这几个字母。 Model 是用来针对数据库,同时它可以用来自动生成管理界面, View 在前面我们一直都用它,用来处理请求和响应的相当于MVC框架中的 Controller 的作用, Template 用来生成界面。
创建 wiki app
==============
::
manage.py startapp wiki
.. note:: 在 0.91 版, app 都是放在 apps 目录下的。不过到了 0.95 版,apps 目录不自动创建了。因此你就可以直接放在项目目录下了。
这样在 ``wiki`` 子目录下有以下文件:
``__init__.py``
表示 ``wiki`` 目录是一个包。
``views.py``
用来放它的 view 的代码。
``models.py``
用来放 model 代码。
编辑 wiki/models.py
==========================
::
from django.db import models
# Create your models here.
class Wiki(models.Model):
pagename = models.CharField(maxlength=20, unique=True)
content = models.TextField()
每个 model 其实在 Django 中就是一个表,你将用它来保存数据。在实际的应用中,一般都要与数据库打交道,如果你不想用数据库,那么原因可能就是操作数据库麻烦,创建数据库环境也麻烦。但通过 Django 的 model 处理,它是一种 ORM (Object Relation Mapping, 对象与关系的映射),可以屏蔽掉底层数据库的细节,同时提供以对象的形式来处理数据。非常方便。而且 Django 的 model 层支持多种数据库,如果你改变数据库也不是什么问题,这也为以后的数据库迁移带来好处。总之,好处多多,大家多多体会吧。
``Wiki`` 是 model 的名字,它需要从 ``models.Model`` 派生而来。它定义了两个字段,一个是字段是 ``pagename`` , 用来保存 wiki 页面的名字,它有两个参数,一个是最大长度(不过从这点上不如 SQLAlchemy_ 方便, SQLAlchemy并不需要长度,它会根据有无长度自动转为 ``TEXT`` 类型),目前 ``CharField`` 需要这个参数;另一个是 ``unique`` 表示这个字段不能有重复值。还有一个字段是 ``content`` ,用来保存 wiki 页面的内容,它是一个 ``TextField`` 类型,它不需要最大长度。
.. _SQLAlchemy: http://www.sqlalchemy.org/
.. note:: models.Model 在 0.91 版是 meta.Model 。而 django.db 在 0.95 版是 django.core 。
现在不太了解 model 没有关系,关键是看整个生成过程。
一旦你定义好了 model ,在运行时, Django 会自动地为这个 model 增加许多数据操作的方法。关于 model 和 数据库操作API的详细内容参见 `Model reference`_ 和 `Database API reference`_ 的文档。
.. _`Model reference`: http://www.djangoproject.com/documentation/model_api/
.. _`Database API reference`: http://www.djangoproject.com/documentation/db_api/
修改 settings.py, 安装 app
===========================
虽然我们的其它工作没有做完,但我还是想先安装一下 app 吧。每个一 app 都需要安装一下。安装一般有两步:
a) 修改settings.py
::
INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'newtest.wiki',
)
这个在文件的最后,前4个是缺省定义的。给出指定 wiki 包的引用名来。这一步是为了以后方便地导入所必须的。因为我们的目录都是包的形式,因此这里就是与目录相对应的。
b) 执行(在newtest目录下)
::
manage.py syncdb
.. note:: 以前是使用 install wiki 。现在也可以使用,不过使用 syncdb 要更简单得多。
如果没有报错就是成功了。这一步 Django 将根据 model 的信息在数据库中创建相应的表。表就是这样创建出来的。
在命令行下加入首页(FrontPage)
===============================
我们假设首页的名字为 FrontPage ,并且我们将在命令行下增加它,让我们熟悉一下命令行的使用
进入 ``newtest`` 目录,然后::
manage.py shell
.. _NewEdit: http://wiki.woodpecker.org.cn/moin/NewEdit
进入 python
::
>>> from newtest.wiki.models import Wiki
>>> page = Wiki(pagename='FrontPage', content='Welcome to Easy Wiki')
>>> page.save()
>>> Wiki.objects.all()
[ {{ content }}
"""
t = loader.get_template(template)
content = r.sub(r'\1', page.content)
content = re.sub(r'[\n\r]+', '
', content)
c = Context({'pagename':page.pagename, 'content':content})
return HttpResponse(t.render(c))
.. note:: 将原来老的 model 方法加了注释,目前改用最新的 API 了。
代码有些长,有些地方已经有说明和注释了。简单说一下:
* ``index()`` 用来显示一个 wiki 页面。它需要一个参数就是页面的名称。如果在数据库中找得到,则调用 ``process()`` 方法(``process()`` 方法是一个自定义方法,主要用来对页面的文本进行处理,比如查找是否有满足 wiki 命名规则的单词,如果有则替换成链接。再有就是将回车转为 ``
`` )。如果没有找到,则直接调用编辑模板显示一个编程页面。当然,这个页面的内容是空的。只是它的页面名字就是 ``pagename`` 。如果 ``pagename`` 为空,则进入 ``FrontPage`` 页面。 ``Wiki.objects`` 对象有 ``filter()`` 方法和 ``get()`` 方法,一个返回一个结果集,一个返回指定的对象。这里为什么使用 ``filter()`` 呢,因为一旦指定文件不存在,它并不是返回一个 ``None`` 对象,而是抛出异常,而我没有使用异常的处理方式。通过 ``filter()`` 如果存在则结果中应有一个元素,如果不存在则应该是一个 ``[]`` 。这样就知道是否有返回了。
.. note:: ``filter()`` 中使用的参数与一般的 db-api 是一样的,但如果是比较相等,可以为: ``pagename__exact=pagename`` 也可以简化为 ``pagename=pagename`` 。
.. note:: 在 Django 中,一些字段的比较操作比较特殊,它是在字段名后加 ``__`` 然后是比较条件。这样看上去就是一个字符串。具体的参见 `The Database API`_ 。
.. _`The Database API`: http://www.djangoproject.com/documentation/db_api/
.. note:: 回车转换的工作其实可以在模板中使用 filter 来完成。
* 从对模板的使用 (wiki/edit.html) 可以猜到在后面我们要在 ``templates`` 中创建子目录了。的确,对于不同的 app ,我们可以考虑将所有的模板都放在统一的 ``templates`` 目录下,但为了区分方便,一般都会针对 app 创建不同的子目录。当然也可以不这样,可以放在其它的地方,只要修改 ``settings.py`` ,将新的模板目录加进去就行了。
因为我们在设计 model 时已经设置了 ``pagename`` 必须是唯一的,因此一旦 ``filter()`` 有返回值,那它只能有一个元素,而 ``pages[0]`` 就是我们想要的对象。
* ``page = wikis.get(pagename='FrontPage')``
是表示取出 ``pagename`` 为 ``FrontPage`` 的页面。你可能要说,为什么没有异常保护,是的,这也就是为什么我们要在前面先要插条记录在里面的原因。这样就不会出错了。再加上我要做的 wiki 不提供删除功能,因此不用担心会出现异常。
* ``edit()`` 用来显示一个编辑页面,它直接取出一个页面对象,然后调用 wiki/edit.html 模板进行显示。也许你还是要问,为什么不考虑异常,因为这里不会出现。为什么?因为 ``edit()`` 只用在已经存在的页面上,它将用于存在页面的修改。而对于不存在的页面是在 ``index()`` 中直接调用模板来处理,并没有直接使用这个 ``edit()`` 来处理。也许你认为这样可能不好,但由于在 ``edit()`` 要重新检索数据库,而在 ``index()`` 已经检索过一次了,没有必要再次检索,因此象我这样处理也没什么不好,效率可能要高一些。当然这只是个人意见。
* ``save()`` 用来在编辑页面时用来保存内容的。它先检查页面是否在数据库中存在,如果不存在则创建一个新的对象,并且保存。注意,在 Django 中,对对象处理之后只有调用它的 ``save()`` 方法才可以真正保存到数据库中去。如果页面已经存在,则更新页面的内容。处理之后再重定向到 ``index()`` 去显示这个页面。
在 templates 中创建 wiki 子目录
================================
编辑 templates/wiki/page.html
================================
::
{{ pagename }}