Size: 35289
Comment: pylons : QuickWiki Tutorial
|
Size: 34897
Comment:
|
Deletions are marked like this. | Additions are marked like this. |
Line 3: | Line 3: |
= QuickWiki Tutorial = * author: James Gardner |
= QuickWiki Tutorial [ for pylons ] = |
Line 6: | Line 5: |
* 作者: James Gardner * 翻译: ["huangyi"] |
|
Line 9: | Line 10: |
If you haven't done so already read the `installation instructions <install.html>`_ and `getting started <getting_started.html>`_ guide. In this tutorial we are going to create a working wiki from scratch using Pylons 0.9 and SQLAlchemy. Our wiki will allow visitors to add, edit or delete formatted wiki pages. |
请先阅读 [http://www.pylonshq.com/docs/0.9.1/install.html installation instructions] 和 [http://www.pylonshq.com/docs/0.9.1/getting_started.html getting started] 两份指南 , 如果你还没读过的话 :) 在这个教程中我们要使用 Pylons0.9 和 SQLAlchemy 从头创建一个可以工作的 wiki. 我们的 wiki 可以允许用户添加,修改,删除带有格式的wiki页面. |
Line 15: | Line 18: |
Pylons is designed to be easy for everyone, not just developers, so lets start by downloading and installing the finished QuickWiki in exactly the way end users of QuickWiki might do. Once we have explored its features we will set about writing it from scratch. After you have installed `Easy Install <http://peak.telecommunity.com/DevCenter/EasyInstall>`_ run these commands to install QuickWiki and create a config file:: |
Pylons 的设计目标是要方便所有人, 而不仅仅是开发者, 所有让我们以 QuickWiki 最终用户的身份下载并安装一个 QuickWiki 的成品先. 等我们发掘完它的特性后, 我们就从头开始编写它. 先安装 [http://peak.telecommunity.com/DevCenter/EasyInstall Easy Install] , 然后运行以下命令来安装 QuickWiki 并创建一个配置文件: {{{ |
Line 21: | Line 25: |
Next edit the configuration file by specifying the ``dsn`` variable in ``[app:main]`` section so that the data source name points to the database you wish to use. .. Note:: The default data source name ``dsn = postgres://@localhost/quickwiki_test`` uses a PostgreSQL database named ``quickwiki_test`` running on the local machine. You can create this database with the PostgreSQL command ``createdb quickwiki_test`` but you could also use MySQL, Oracle or SQLite. Firebird and MS-SQL may also work. See the `SQLAlchemy documentation <http://www.sqlalchemy.org/docs/dbengine.myt#dbengine_establishing>`_ for more information on how to connect to different databases. SQLite for example requires four forward slashes in its DSN. You will also need to make sure you have the appropriate Python driver for the database you wish to use. For PostgreSQL this would be `psycopg <http://www.initd.org/projects/psycopg1>`_. Finally create the database tables and serve the finished application:: |
}}} 下面编辑该配置文件, 在 {{{[app:main]}}} 节中指定 {{{dsn}}} 变量, 让数据源的名称指向你希望使用的数据库. * 注意: 默认的数据源名称 {{{dsn = postgres://@localhost/quickwiki_test}}} 使用一个叫 {{{quickwiki_test}}} 的在本地运行的 PostgreSQL 数据库. 你可以使用 PostgreSQL 的命令 createdb quickwiki_test 来创建这个数据库, 你也可以使用 MySQL, Oracle 或者 SQLite. Firebird 和 MS-SQL 应该也能行. 连接不同数据库的详细信息请查看 [http://www.sqlalchemy.org/docs/dbengine.myt#dbengine_establishing SQLAlchemy documentation]. 比如 SQLite 的 dsn 里需要有4个反斜线. 你还得确定你已经有了你要使用的数据库的相应的 python驱动. 比如用 PostgreSQL 的话你就需要 [http://www.initd.org/projects/psycopg1 psycopg] 最后, 创建数据表并启动该成品: {{{ |
Line 30: | Line 36: |
That's it! Now you can visit http://127.0.0.1:5000 and experiment with the finished Wiki. Note that in the title list screen you can drag page titles to the trash area to delete them via AJAX calls. When you've finished, stop the server with ``CTRL+C`` because we will start developing our own version. |
}}} 这样就OK了 ! 现在你可以访问 http://127.0.0.1:5000 并实验这个 Wiki 成品. 注意, 在标题列表页, 只要将页面标题拖到垃圾区域, 就可以通过 Ajax 调用删除它. 弄完这些以后, 使用 {{{CTRL+C}}} 停止服务器, 因为我们要开始开发我们自己的版本了. |
Line 37: | Line 43: |
If you skipped the section above you will need to install Pylons 0.9:: |
如果你跳过了上面这一节那你需要安装 Pylons 0.9 先: {{{ |
Line 40: | Line 47: |
Then create your project:: |
}}} 然后创建你的 project : {{{ |
Line 44: | Line 53: |
Now lets start the server and see what we have:: |
}}} 我们启动服务器看看现在有了点啥: {{{ |
Line 49: | Line 60: |
.. Note:: We have started the server with the ``--reload`` switch. This means any changes we make to code will cause the server to restart (if necessary); your changes are immediately reflected on the live site. Open a new console and ``cd QuickWiki/quickwiki``. Visit http://127.0.0.1:5000 where you will see the introduction page. Delete the file ``public/index.html`` because we want to see the front page of the wiki instead of this welcome page. If you now refresh the page, the Pylons built-in error document support will kick in and display an ``Error 404`` page to tell you the file could not be found. We'll setup a controller to handle this location later. |
}}} * 注意: 我们使用了{{{--reload}}}选项启动服务器. 这意味着这以后我们对代码做的任何更改都会导致服务器重启(如果有必要重启的话); 你的改变可以实时得在当前站点上反映出来. 开一个新的控制台并执行{{{ cd QuickWiki/quickwiki }}}. 访问 http://127.0.0.1:5000 你就可以看到介绍页面. 删除文件 {{{public/index.html}}} ,因为我们想看到 wiki 的首页而不是欢迎页面. 如果你现在就刷新页面的话, Pylons 内置的错误文档会起作用并显示一个 {{{ Error 404 }}} 的页面, 告诉你该文件找不到. 待会我们会建立一个 controller 来处理对这个地址的访问. |
Line 56: | Line 68: |
Pylons uses a Model View Controller architecture; we'll start by creating the model. We could use any system we like for the model including `SQLObject <http://www.sqlobject.org>`_ or `SQLAlchemy <http://www.sqlalchemy.org>`_. We'll use SQLAlchemy for QuickWiki. .. Note:: SQLAlchemy is a Python SQL toolkit and Object Relational Mapper that is fast becoming the default choice for many Pylons programmers. It provides a full suite of well known enterprise-level persistence patterns, designed for efficient and high-performance database access, adapted into a simple and Pythonic domain language. There is full and detailed documentation available on the SQLAlchemy website at http://sqlalchemy.org/docs/ and you should really read this before you get heavily into SQLAlchemy. Edit your ``models/__init__.py`` so that it looks like: .. code-block:: Python |
Pylons 使用的是一个 Model View Controller 的架构; 我们从创建 model 开始. 我们可以使用任何我们喜欢的系统来作为我们的 model 层, 包括 [http://www.sqlobject.org SQLObject] 或者 [http://www.sqlalchemy.org SQLAlchemy]. 下面我们将使用 SQLAlchemy 来开发 QuickWiki. * 注意: SQLAlchemy 是一个 Python SQL工具包 和 Object Relational Mapper , 它正迅速成为许多 Pylons 开发者的默认选择. 它提供了一套完整的众所周知的企业级持久层模式的实现, 设计目标是高效和高性能的数据库访问, 并向外提供一个简单的Pythonic的领域语言 (adapted into a simple and Pythonic domain language). 在 SQLAlchemy 的网站 http://sqlalchemy.org/docs/ 上有完整的详细的文档, 强烈建议在你大量使用 SQLAlchemy 之前看看这些文档先. 将 {{{models/__init__.py}}} 编辑成下面这样: {{{#!python |
Line 75: | Line 84: |
The first two lines setup SQLAlchemy to support threads and import some useful objects such as the ``Table`` and ``Column`` classes. The second line ``meta = DynamicMetaData()`` is so that we can bind the tables to a database later on. We then define a table called ``pages`` which has two columns, ``title`` (the primary key) and ``content``. .. Note:: SQLAlchemy also supports reflecting this information directly from a database table so if we had already created the database table SQLAlchemy could have constructed the ``Table`` object for us. A core philosophy of SQLAlchemy is that tables and domain classes are different beasts. So next, we'll create the Python class that will represent the pages of our wiki and map these domain objects to rows in the ``pages`` table using a mapper. Add this to the bottom of ``models/__init__.py``: .. code-block:: Python |
}}} 前面两行将 SQLAlchemy 设置为支持线程, 并导入一些有用的对象, 比如 {{{Table}}} 和 {{{Column}}}. 第二行 {{{meta = DynamicMetaData()}}} 是让我们可以在以后将数据表绑定到特定的数据库上去. 然后我们定义了一个叫做 {{{pages}}} 的表, 它有两列: {{{title}}} 和 {{{content}}}. * 注意: SQLAlchemy 也支持直接从数据库表中反射这些信息, 如果我们已经创建好了数据库的话,SQLAlchemy 可以自动构造 {{{Table}}} 对象. SQLAlchemy 一个核心的哲学就是 数据表 和 领域类 是两个不同的东西. 下面, 我们就要创建一个 Python 类, 它表示 wiki 中的页面,并且会通过一个 mapper 将一个领域对象映射为 {{{pages}}} 表中的一行. 将下面这些代码添加到 {{{models/__init__.py}}} 的底部: {{{#!python |
Line 89: | Line 98: |
Looking ahead, our wiki will need some formatting so we will need to turn the ``content`` field into HTML. Any WikiWords (which are words made by joining together two or more lowercase words with the first letter capitalized) will also need to be converted into hyperlinks. It would be nice if we could add a method to our ``Page`` object to retrieve the formatted HTML with the WikiWords already converted to hyperlinks. Add the following at the top of the ``models/__init__.py`` file: .. code-block:: Python |
}}} 稍微想远一点, 我们的 wiki 将会需要一些格式, 所以我们需要将 {{{content}}} 字段转换成 HTML. 任何 WikiWords (就是那些由一个大写字母和几个小写字母组成的单词连接起来组成的 which are words made by joining together two or more lowercase words with the first letter capitalized) 也需要转换成超链接. 要是我们可以给我们的 {{{Page}}} 对象添加一个方法,返回格式化好的 HTML 该多好啊. 将以下代码加到 {{{models/__init__.py}}} 的头部: {{{#!python |
Line 100: | Line 109: |
and then add a ``get_wiki_content()`` method to the ``Page`` object so it looks like this: .. code-block:: Python |
}}} 然后给{{{Page}}}对象添加一个 {{{get_wiki_content()}}} 方法, 像这样: {{{#!python |
Line 128: | Line 137: |
This code deserves a bit of explaining. The ``content = None`` line is so that the ``content`` attribute is initialized to ``None`` when a new ``Page`` object is created. If the ``Page`` object represents a row in the ``pages`` table then ``self.content`` will be the value of the ``content`` field. ``h.link_to()`` and ``h.url_for()`` are standard Pylons helpers which create links to specific controller actions. In this case we have decided that all WikiWords should link to the ``index`` action of the ``page`` controller which we will create later. One final change, since we have used docutils and SQLAlchemy, both third party packages, we need to edit our ``setup.py`` file so that anyone installing QuickWiki with `Easy Install <http://peak.telecommunity.com/DevCenter/EasyInstall>`_ will automatically also have these dependencies installed for them too. Edit your ``setup.py`` in your project root directory so that the ``install_requires`` line looks like this:: |
}}} 这些代码值得解释一下.{{{content = None}}}这一行是在创建一个新的{{{Page}}}对象的时候将属性{{{content}}}初始化为{{{None}}}. 如果{{{Page}}}对象表示{{{pages}}}表中的一行,那么{{{self.content}}}就是字段{{{content}}}的值. {{{h.link_to()}}} 和 {{{h.url_for()}}} 是标准的 Pylons helpers, 这两个函数创建指向特定controller actions的链接. 现在我们已经决定所有 WikiWords 应该链接到 {{{page}}} controller 的 {{{index}}} action , 这个 controller 我们会在后面创建. 最后再做一点修改, 既然我们使用到了 docutils 和 SQLAlchemy 两个第三方的库, 那么我们还需要修改下我们的 {{{setup.py}}} 文件, 这样当别人通过[http://peak.telecommunity.com/DevCenter/EasyInstall Easy Install] 安装 QuickWiki 的时候就会自动安装依赖的这些库. 修改项目根目录下的{{{setup.py}}}, 将其中 {{{install_requires}}} 修改为如下: {{{#!python |
Line 134: | Line 145: |
While we are we are making changes to ``setup.py`` we might want to complete some of the other sections too. Set the version number to 0.1.1 and add a description and URL which will be used on the Python Cheeseshop when we release it:: |
}}} 既然我们正在修改 {{{setup.py}}} ,干脆把其他一些修改也一起做了吧. 将版本号设置为 0.1.1, 并添加一个描述和URL, 当我们把它发布在Python Cheeseshop 上的时候会用得上: {{{#!python |
Line 140: | Line 153: |
We might also want to make a full release rather than a development release in which case we would remove the following lines from ``setup.cfg``:: |
}}} 我们可能也希望制作一个完整的发布版而不是一个开发版, 这样的话我们就把 {{{setup.cfg}}} 里的下面几行移除: {{{ |
Line 146: | Line 161: |
To test the automatic installation of the dependencies, run the following command which will also install docutils and SQLAlchemy if you don't already have them:: |
}}} 要想测试下依赖库的自动安装的话, 运行下面的命令, 他们会自动安装 docutils 和 SQLAlchemy ,如果你还没有装过的话: {{{ |
Line 150: | Line 167: |
.. Note:: The command ``python setup.py develop`` installs your application in a special mode so that it behaves exactly as if it had been installed as an egg file by an end user. This is really useful when you are developing an application because it saves you having to create an egg and install it every time you want to test a change. | }}} * 注意: 使用命令 {{{python setup.py develop}}} 安装该应用程序, 和最终用户通过egg文件安装的情形是一样的. 当你开发应用程序的时候这是很有用的,因为你不用每次修改了程序后想测试一下的时候都必须另外创建一个egg并安装才行. |
Line 155: | Line 173: |
All of your project's controllers are derived from the ``BaseController`` in ``lib/base.py`` by default. We will make use of this fact to enable all controllers to with SQLAlchemy support. Add the following code underneath the import statements in ``lib/base.py`` to replace the existing ``BaseController`` class: .. code-block:: Python |
你的项目中所有的 controllers 默认都继承自 {{{lib/base.py}}} 文件中的 {{{BaseController}}}. 我们要利用它来给所有 controllers 加上对 SQLAlchemy 的支持. 将下面这些代码加在 {{{lib/base.py}}} 的 import 语句后面,覆盖已有的 {{{BaseController}}} 类: {{{#!python |
Line 171: | Line 188: |
On each request the SQLAlchemy objects will now be connected to the database. |
}}} 现在每次用户请求, SQLAlchemy的对象都会连接到数据库了. |
Line 176: | Line 194: |
Now lets make the changes necessary to enable QuickWiki to be set up by an end user. Edit ``websetup.py`` used by the ``paster setup-app`` command to look like this: .. code-block:: Python |
现在让我们再做一点修改, 目的是让最终用户能够将 QuickWiki 安装起来. 将 {{{websetup.py}}} ( 命令 {{{paster setup-app}}} 将会使用该函数 ) 修改为下面这样: {{{#!python |
Line 197: | Line 214: |
The line ``from quickwiki.models import *`` imports our ``page`` table, the line ``meta.connect(app_conf['dsn'])`` connects to the database with the DSN the user specifies in the config file and finally ``meta.create_all()`` creates the table. After the tables are created the code adds some data for the simple front page to out wiki. To test this functionality run you first need to install your QuickWiki if you haven't already done so in order for ``paster`` to find the version we are developing instead of the version we installed at the very start:: |
}}} {{{from quickwiki.models import *}}} 这一行导入我们的 {{{page}}} 表, {{{meta.connect(app_conf['dsn'])}}} 这一行使用 用户在配置文件中指定的 DSN 连接数据库. 最后 {{{meta.create_all()}}} 创建数据表. 数据表创建完成后,代码还为简单的 wiki 首页添加了点数据. 要测试上面这个功能的话, 你首先需要安装你的 QuickWiki (如果你还没这么做过的话), 目的是让 {{{paster}}} 找到我们正在开发的版本而不是我们在教程最开始的时候安装的版本: {{{ |
Line 203: | Line 222: |
You can then edit your ``development.ini`` to add your database DSN: .. code-block:: PasteIni |
}}} 然后你就可以编辑你的 {{{development.ini}}} , 并添加你的数据库 DSN: {{{ |
Line 211: | Line 230: |
You should also edit ``QuickWiki.egg_info/paste_deploy_config.ini_tmpl`` so that when users run ``paster make-config`` the configuration file will already have a section telling them to enter their own database DSN as we did when we installed the finished QuickWiki at the start of the tutorial: .. code-block:: PasteIni |
}}} 你还需要修改 {{{QuickWiki.egg_info/paste_deploy_config.ini_tmpl}}} 如下, 这样当用户运行命令 {{{paster make-config}}}时 , 生成的配置文件总是会有一个节提示他们输入他们自己的数据库的DSN , 就像我们在一开始安装 QuickWiki 成品时所做的一样: {{{ |
Line 220: | Line 239: |
You can should then run the ``paster setup-app`` command to setup your tables in the same way and end user would, remembering to drop and recreate the database if the version tested earlier has already created the tables:: |
}}} 然后你就可以运行命令 {{{paster setup-app}}} 来建立你的数据表,就和最终用户将会做的一样. 如果以前版本的程序已经创建过数据表的话, 记得要删除并重建数据库. (译注:如果没有修改过model的话,就不需要) {{{ |
Line 224: | Line 245: |
.. Note:: See the SQLAlchemy note in the `Starting at the End`_ section for information on supported DSNs and a link to the SQLAlchemy documentation about the various options that can be included in them. | }}} * 注意: 关于具体支持哪些 DSN 的信息请查看第一段中关于 SQLAlchemy 的 "注意" , 和包含在其中的指向 SQLAlchemy文档中关于DSN不同选项部分的链接. |
Line 229: | Line 251: |
.. Note:: Pylons uses the Myghty templating language by default, although as is the case with most aspects of Pylons you are free to deviate from the default if you prefer. Pylons also supports Kid and Cheetah out of the box. We will make use of a feature of the Myghty templating language called inheritance for our project. Add the main page template in ``templates/autohandler``: .. code-block:: HyperText |
* 注意: Pylons 默认使用 Myghty 模板语言, 但是只要你愿意你可以方便地使用其他的模板语言, 同时不影响大部分 Pylons 的功能. Pylons另外还支持 Kid 和 Cheetah * Note: See the SQLAlchemy note in the `Starting at the End`_ section for information on supported DSNs and a link to the SQLAlchemy documentation about the various options that can be included in them. 我们会在我们的项目中用到 Myghty 模板语言 的一个叫做继承的特性. 首先在 {{{templates/autohandler}}} 中添加基页面模板: {{{ |
Line 251: | Line 273: |
All our other templates will be automatically inserted into the ``% m.call_next()`` line and the whole page returned when we call the ``render_response()`` global from our controller so that we can easily apply a consistent theme to all our templates. If you are interested in learning some of the features of Myghty templates have a look at the comprehensive `Myghty Documentation <http://www.myghty.org/docs/>`_. For now we just need to understand that is replaced with the child template and that anything in ``<%`` and ``%>`` brackets is executed and replaced with the result. Having the ``%`` as the first character on a line is shorthand for putting the whole line in ``<%`` and ``%>`` brackets. This autohandler also makes use of various helper functions attached to the ``h`` object. These are described in the `WebHelpers documentation <http://pylonshq.com/WebHelpers/module-index.html>`_. You can add more helpers to the ``h`` object by adding them to ``lib/helpers.py`` although for this project we don't need to do so. .. Warning:: Your templates won't work if there is any whitespace before lines starting with a ``%`` character. |
}}} 所有其他的模板会自动地被插入到 {{{% m.call_next()}}} 这行, 并且当我们在控制器中调用 {{{render_response()}}} 的时候会返回整个页面, 这样我们就可以方便地给我们所有的模板应用统一的主题了. 如果你有兴趣学习 Myghty模板 的其他一些特性的话可以去看看易懂的 [http://www.myghty.org/docs/ Myghty 文档]. 现在我们只需要理解那一行会被子模板替换 和 任何在 {{{<%}}} 和 {{{%>}}} 之间的部分会被当作python程序执行然后被替换成执行结果. 在一行的第一个字符处写 {{{%}}} 等于是用 {{{<%}}} 和 {{{%>}}} 把整行扩起来. 这个 autohandler 用到几个被附在 {{{h}}} 对象上的 helper 函数. 这些函数在[http://pylonshq.com/WebHelpers/module-index.html WebHelpers 文档]中有详细描述. 你还可以向 {{{h}}} 上附加更多的helpers, 只需把他们写到 {{{lib/helpers.py}}} 中即可, 虽然这个项目中我们还不需要. * 警告:如果在 {{{%}}} 字符前有空格的话, 你的模板将不起作用. |
Line 262: | Line 285: |
Before we can add the actions we want to be able to route the requests to them correctly. Edit ``config/routing.py`` so that the route map looks like this: .. code-block:: Python |
在我们添加任何 action 之前我们希望能够将请求正确的路由( route )到这些 action . 编辑 {{{config/routing.py}}} 文件, 把 route map 改成这样: {{{#!python |
Line 270: | Line 292: |
Full information on the powerful things you can do to route requests to controllers and actions can be found in the `Routes documentation <http://routes.groovie.org/docs/>`_. | }}} 关于强大的将请求路由(route) 到 controllers 和 actions 的功能的完整文档在这里: [http://routes.groovie.org/docs/ Routes文档]. |
Line 275: | Line 298: |
Quick Recap: We've setup the model, configured the application, added the routes and setup the base template in autohandler, now we need to write the application! In your project's root directory add a controller called ``page`` to your project with this command:: |
简单回顾: 我们已经建立了 model, 配置好了应用程序, 添加了 routes 并在 autohandler 中设置了基模板, 现在我们需要编写项目了! 在你的项目的根目录下使用下面这个命令添加一个叫做{{{page}}}的 controller : {{{ |
Line 278: | Line 302: |
We are going to need the following actions:: ``index(self, title)`` |
}}} 我们需要下面几个 actions: {{{#!python index(self, title) |
Line 284: | Line 310: |
``edit(self, title)`` displays a from for editing the page ``title`` ``save(self, title)`` save the page ``title`` and show it with a saved message ``list(self)`` |
edit(self, title) displays a from for editing the page title save(self, title) save the page title and show it with a saved message list(self) |
Line 293: | Line 319: |
``delete(self)`` | delete(self) |
Line 295: | Line 321: |
Let's get cracking! Add the following import statements to the top of the page controller ``controllers/page.py`` after the existing import statement: .. code-block:: Python |
}}} Let's get cracking! 在 page controller {{{controller/page.py}}} 的顶端紧靠现有 import 语句下面的地方添加以下几个 import 语句: {{{#!python |
Line 302: | Line 328: |
Add the this method to the ``PageController`` class to create a query object for the ``Page`` on each request: .. code-block:: Python |
}}} 将这个方法添加到 {{{PageController}}} 类, 在每个请求到来之时为 {{{Page}}} 创建一个 {{{query}}} 对象: {{{#!python |
Line 309: | Line 335: |
.. Note:: ``__before__()`` is a special action which is always called on each request before the main action is called. Likewise there is also an ``__after__()`` action called after your main action is called. |
}}} * 注意: {{{__before__()}}} 是一个特殊的 action, 每次请求到来地时候, 在调用实际 action 之前都会先调用 {{{__defore__}}, 还有一个 {{{__after__}}} 则是在调用实际 action 之后调用. |
Line 314: | Line 341: |
Replace the existing ``index()`` action with this: .. code-block:: Python |
用下面代码替换 {{{index()}}} action: {{{#!python |
Line 326: | Line 352: |
}}} | |
Line 327: | Line 354: |
Add a template called ``templates/page.myt`` that looks like this: .. code-block:: HyperText |
添加一个叫 {{{templates/page.myt}}} 的模板,内容如下: {{{ |
Line 333: | Line 359: |
This template simply displays the page title and content. .. Note:: Pylons automatically assigns all the action parameters to the context object ``c`` so that you don't have to assign them yourself. In this case, the value of ``title`` will be automatically assigned to ``c.title`` so that it can be used in the templates. We assign ``c.content`` manually in the controller. We also need a template for pages that don't already exist. They need to display a message and link to the edit action so that they can be created. Add a template called ``templates/new_page.myt`` that looks like this: .. code-block:: HyperText |
}}} 这个模板只是简单地显示页面的 title 和 content. * 注意: Pylons 自动把所有 action 参数赋给 context object {{{c}}}, 这样你就不用亲自动手了. 这样, {{{title}}} 的值自动赋给 {{{c.title}}} , 可以直接在模板中使用了. 我们还得在 controller 中手动给 {{{c.content}}} 赋值 (译注: 对象 {{{c}}} 可以直接在模板中使用, 用来向模板中传递数据) 我们还需要为页面不存在的情况编写一个模板. 需要在上面显示一条信息和一个到 edit action 的链接, 这样该页面才可以被创建. 添加一个叫 {{{templates/new_page.myt}}} 的模板, 内容如下: {{{ |
Line 345: | Line 371: |
At this point we can test our QuickWiki to see how it looks. If you don't already have a the server running start it now with:: |
}}} 现在我们可以测试我们的 QuickWiki 了 , 看看现在怎么样了. 如果你还没有把服务器运行起来的话,使用下面命令运行它: {{{ |
Line 349: | Line 377: |
Visit http://127.0.0.1:5000/ and you will see the front page of the wiki. We can spruce it up a little by adding the stylesheet we linked to in the ``templates/autohandler`` file earlier. Add this the file ``public/quick.css`` with the following content: .. code-block:: CSS |
}}} 访问 http://127.0.0.1:5000/ 你就可以看到 wiki 的首页了. 我们可以添加一个已经在 {{{templates/autohandler}}} 中链进来了的 css 把它变漂亮一点点. 把以下内容加到文件 {{{public/quick.css}}} 中: {{{ |
Line 375: | Line 403: |
Refresh the page again and we have a better looking wiki. You will notice that the word ``QuickWiki`` has been turned into a hyperlink by the ``get_wiki_content()`` method we added to our ``Page`` domain object earlier. You can click the link and will see an example of the new page screen from the ``new_page.myt`` template. If you follow the ``Create the page`` link you will see the Pylons automatic error handler kick in to tell you ``Action edit is not implemented``. Well, we better write it next but before we do, have a play with the `interactive debugger <interactive_debugger.html>`_, try clicking on the ``+`` or ``>>`` arrows and you will be able to interactively debug your application it is a tremendously useful tool. | }}} 刷新页面, 这时我们便有了一个更漂亮的 wiki 了. 你会注意到单词 {{{QuickWiki}}} 已经被 {{{Page}}} 领域对象的 {{{get_wiki_content()}}} 方法转换成一个超链接了. 你可以点击该链接然后会看到一个来自 {{{new_page.myt}}} 模板的新建页面的页面. 如果你点击 {{{Create the page}}} 链接你会看到 Pylons 的错误处理起作用并提示你 {{{Action edit is not implemented}}}. Well, 下面我们最好把它加上, 不过加它之前, 我们先玩一玩这个 [http://www.pylonshq.com/docs/0.9.1/interactive_debugger.html interactive debugger], 试着点击 {{{+}}} 或者 {{{>>}}} 箭头 , 你就可以交互式地调试你的应用程序, 这是一个相当有用的工具. |
Line 379: | Line 409: |
To edit the wiki page we need to get the content from the database without changing it to HTML to display it in a simple form for editing. Add the ``edit()`` action: .. code-block:: Python |
要编辑 wiki 页面, 我们需要先从数据库中取得没被转成HTML的内容, 并把它显示在一个简单的 form 里面等候编辑. 添加 {{{edit()}}} action: {{{#!python |
Line 388: | Line 417: |
and the create the ``templates/edit.myt`` file: .. code-block:: HyperText |
}}} 并创建 {{{templates/edit.myt}}} 文件: {{{ |
Line 399: | Line 428: |
.. Note:: You might have noticed that we only set ``c.content`` if the page exists but that it is accessed in ``h.text_area`` even for pages that don't exist and doesn't raise an ``AttributeError``. We are making use of the fact that the ``c`` object returns an empty string ``""`` for any attribute that is accessed. This is a very useful feature of the ``c`` object but can catch you out on occasions where you don't expect this behavior. We are making use of the ``h`` object to create our form and field objects. This saves a bit of manual HTML writing. The form submits to the ``save()`` action to save the new or updated content so let's write that next. |
}}} * 注意: 你可能已经注意到我们只在页面存在的情况下设置 {{{c.content}}}, 但是在页面不存在的情况下 {{{c.content}}} 被 {{{h.text_area}}} 访问也没有抛出 {{{AttributeError}}} 异常. 我们其实是利用了这么一个规定: 对象 {{{c}}} 任何被访问的属性总是默认返回空字符串 {{{""}}} . 这是对象 {{{c}}} 一个很有用的特性, 不过如果你期望的不是这种行为的话可能会让你很郁闷( catch you out ) 我们使用到 {{{h}}} 对象来创建我们的 form 和 field 对象. 这个节省了一点点手工编写 HTML 的时间. 该 form 提交到 {{{save()}}} action 以保存刚刚新建的或更新过的内容, 下面我们来编写 {{{save()}}} . |
Line 406: | Line 436: |
The first thing the ``save()`` action has to do is to see if the page being saved already exists. If not it creates it with ``page = model.Page()``. Next it needs the updated content. In Pylons you can get request parameters from form submissions, GET or POST requests from the appropriately named ``request.params`` object. Add the ``save()`` action: .. code-block:: Python |
{{{save()}}} action 需要做的第一件事就是要检查要保存的页面是否已经存在. 如果不存在则使用 {{{page = model.Page()}}} 创建新页面. 然后再更新其内容. 在 Pylons 中你可以从 form 提交 中获取请求参数, 从相应名字的 {{{request.params}}} 对象中可以获取 GET 或者 POST 请求. 添加 {{{save()}}} action: {{{#!python |
Line 422: | Line 451: |
}}} | |
Line 423: | Line 453: |
.. Note:: ``request.params`` is a MultiDict object; an ordered dictionary that may contain multiple values for each key. The MultiDict will always return one value for any existing key via the normal dict accessors (``params[key]``, ``params.get()`` and ``params.setdefault()``). When multiple values are expected, use the ``getall()`` method to return all values in a list. In order for the ``page.myt`` template to display the ``Successfully saved`` message after the page is saved we need to update the ``templates/page.myt`` file. After ``<h1 class="main"><% c.title %></h1>`` add these lines making sure there is no whitespace before the lines that start with a ``%`` character: .. code-block:: HyperText |
* 注意: {{{request.params}}} 是一个 MultiDict 对象; 一个排好序了的字典, 其中一个 key 可以对应多个值. 使用正常的 dict 访问方法 ({{{params[key]}}}, {{{params.get()}}} 和 {{{params.setdefault()}}}) 访问 MultiDict 可以获取已存在的 key 对应的多个 value 中的一个, 如果想得到多个 value, 使用 {{{getall()}}} 方法, 它会通过一个列表中返回所有 value. 为了让 {{{page.myt}}} 模板在页面保存以后显示 {{{Successfully saved}}} 信息, 我们需要修改 {{{templates/page.myt}}} 文件. 在 {{{<h1 class="main"><% c.title %></h1>}}} 后面添加下面几行, 确保字符 {{{%}}} 前面没有空白: {{{ |
Line 433: | Line 461: |
At this point we have a fully functioning wiki that lets you create and edit pages and can be installed and deployed by an end user with just a few simple commands. Visit http://127.0.0.1:5000 and have a play. It would be nice to get a title list and to be able to delete pages, so that's what we'll do next! |
}}} 现在我们有了一个功能完整的 wiki , 你可以创建并编辑页面, 最终用户还可以通过简单的命令进行安装部署. 访问 http://127.0.0.1:5000 试试看. 要是有一个标题列表, 可以在里面删除页面该多好啊, 下面我们就来增加这个功能 ! |
Line 442: | Line 471: |
Add the ``list()`` action: .. code-block:: Python |
添加 {{{list()}}} action: {{{#!python |
Line 449: | Line 477: |
The ``list()`` action simply gets all the pages from the database. Create the ``templates/titles.myt`` file to display the list: .. code-block:: HyperText |
}}} {{{list()}}} action 简单地从数据库中获取所有页面. 我们创建 {{{templates/titles.myt}}} 文件来显示该列表: {{{ |
Line 463: | Line 491: |
Finally edit ``templates/autohandler`` to add a link to the title list so that the footer looks like this: .. code-block:: HyperText |
}}} 最后编辑 {{{templates/autohandler}}} , 添加一个到标题列表页的链接, 这样页面底部就像这样: {{{ |
Line 476: | Line 504: |
If you visit http://127.0.0.1:5000/page/list you should see the full titles list and you should be able to visit each page. | }}} 如果你访问 http://127.0.0.1:5000/page/list 你应该可以看到完整的标题列表并且你应该可以访问到每个页面. |
Line 481: | Line 510: |
Since this tutorial is designed to get you familiar with as much of Pylons core functionality as possible we will use some AJAX to allow the user to drag a title from the title list into a trash area that will automatically delete the page. Add this line to ``templates/autohandler`` before ``</head>``: .. code-block:: HyperText |
既然设计这个教程的目的是让你熟悉尽可能多的 Pylons核心功能, 那我们要使用一些 AJAX 来允许用户从标题列表拖动一个标题到垃圾区, 并自动删除该 wiki 页. 把下面这行添加到 {{{templates/autohandler}}} 中 {{{</head>}}} 的前面: {{{ |
Line 488: | Line 516: |
.. Note:: The ``h.javascript_include_tag()`` helper will create links to all the built-in JavaScripts we need and also add ``/javascripts/effects.js`` creating HTML that looks like this when you access it from a browser: .. code-block:: HyperText |
}}} * 注意: {{{h.javascript_include_tag()}}} helper 会创建指向所有我们需要的内置 JavaScript 的 link, 另外再添加 {{{/javascripts/effects.js}}} , 生成的 HTML 是这个样子: {{{ |
Line 496: | Line 524: |
If you look at ``config/middleware.py`` you will see these lines: .. code-block:: Python |
}}} 如果你查看 {{{config/middleware.py}}} , 你会看到这么几行: {{{#!python |
Line 504: | Line 532: |
}}} | |
Line 505: | Line 534: |
The ``javascripts_app`` WSGI application maps any requests to ``/javascripts/`` straight to the relevant JavaScript in the WebHelpers package. This means you don't have to manually copy the Pylons JavaScript files to your project and that if you upgrade Pylons, you will automatically be using the latest scripts. Now for the AJAX! We want all the titles in the titles list to be draggable so we enclose each of them with a ``<span>`` element with a unique ID. Edit ``templates/titles.myt`` to replace this line: .. code-block:: HyperText |
WSGI 应用程序 {{{javascripts_app}}} 将所有对 {{{/javascripts/}}} 的请求直接映射为 WebHelpers 包中对应的 JavaScript.这意味这你不需要手动将 Pylons JavaScript 文件复制到你的项目, 并且如果你升级 Pylons , 你自动便可以使用最新的 scripts. 现在我们来编写 AJAX ! 我们想要标题列表中所有的标题是可拖动的, 我们把它们每一个都用一个 id 唯一 {{{<span>}}} 元素包起来. 编辑 {{{templates/titles.myt}}} 把这一行: {{{ |
Line 512: | Line 540: |
with this: .. code-block:: HyperText |
}}} 换成 {{{ |
Line 520: | Line 548: |
This marks each of the titles as a draggable element that reverts to its original position if it isn't dropped over a drop target. If we want to be able to delete the pages we better add a drop target. Try it out at http://127.0.0.1:5000/page/list by dragging the titles themselves around the screen. Notice how much functionality we get with just the one helper ``h.draggable_element()``. We better have somewhere to drop the titles to delete them so add this before the ``<ul id="titles">`` line in ``templates/titles.myt`` : .. code-block:: HyperText |
}}} 该代码将每一个标题变成可拖动的元素, 如果没有拖到拖曳目标的话, 会自动回到原始位置. 如果想要删除页面的话, 我们最好还是添加一个拖曳目标. 现在你已经可以访问 http://127.0.0.1:5000/page/list 拖页面标题玩. 你应该能感受到这一个 helper 函数: {{{h.draggable_element()}}}, 提供了多少功能 ! 我们最好准备个拖曳目标, 把标题拖到那里就自动删掉相应页面, 在 {{{templates/titles.myt}}} 中 {{{<ul id="titles">}}} 这一行前面加上: {{{ |
Line 531: | Line 559: |
We will also need to add the style for the trash box to the end of ``public/quick.css``: .. code-block:: CSS |
}}} 我们还需要为垃圾箱添加 css 样式, 在 {{{public/quick.css}}} 的末尾加上: {{{ |
Line 543: | Line 571: |
.. Tip:: It can sometimes be very hard to debug AJAX applications. Pylons can help. if an error occurs in debug mode (the default in ``development.ini``) a debug URL where you can use an interactive debugger will be printed to the error stream, even in an AJAX request. If you copy and paste that address into a bowser address bar you will be able to debug the request. When a title is dropped on the ``trash`` box an AJAX request will be made to the ``delete()`` action posting an ``id`` parameter with the id of the element that was dropped. The element with id ``titles`` will be replaced with whatever is returned from the action so we better add a ``delete()`` action that returns the new list of titles excluding the one that has been deleted: .. code-block:: Python |
}}} * 技巧: 有的时候调试 AJAX 应用程序是很困难的. Pylons 可以帮你. 在 debug 模式下如果程序发生了错误, 一个包含 interactive debugger 的 debug URL 会被打印到错误流中, 甚至在 AJAX 请求中也是如此. 只要复制粘贴到浏览器地址栏, 你就可以调试该请求了. 当一个标题被拖到垃圾箱后, 会发一个 AJAX 请求到 {{{delete()}}} action, 并通过参数 {{{id}}} 提交该拖动元素的id. 而 id 为 {{{titles}}} 的元素会被从 action 返回的东西替换, 所以我们最好再添加一个 {{{delete()}}} action, 它返回删除的页面后新的标题列表. {{{#!python |
Line 557: | Line 585: |
}}} | |
Line 558: | Line 587: |
The title of the page is obtained from the ``id`` element and the object is loaded and then deleted. The change is saved ``objectstore.flush()`` before the list of remaining titles is rendered by the template ``templates/list.myt`` which you should create with the following content: .. code-block:: HyperText |
从 {{{id}}} 参数中获取页面标题, 然后加载, 然后删除. 对数据库的修改通过调用 {{{objectstore.flush()}}} 应用到数据库. 然后剩下的标题被渲染到模板 {{{templates/list.myt}}} , 该模板使用以下内容创建: {{{ |
Line 569: | Line 597: |
.. Note:: We passed ``fragment=True`` to the ``render_response()`` global. This treats the template as a fragment and does not try to insert its output into the ``autohandler``. If we hadn't used this an entire page would have been returned and put in place of the titles list which clearly isn't what we wanted. Visit http://127.0.0.1:5000/page/list and have a go at deleting some pages. You may need to go back to the FrontPage and create some more if you get carried away! One last tidy up we can make before we finish. You might have noticed that the ``list.myt`` and ``titles.myt`` use the same code to produce the lists. In the interests of keeping code duplication to a minimum edit ``templates/titles.myt`` to replace this code: .. code-block:: HyperText |
}}} * 注意: 我们给 {{{render_response()}}} 全局函数传递了 {{{fragment=True}}} . 它会把模板当作一个片段处理, 不会将输出插入到 {{{autohandler}}}. 如果我们没有用这个参数的话, 整个页面都会被返回并放在标题列表的位置, 这显然不是我们想要的结果. 访问 http://127.0.0.1:5000/page/list 然后删几个页面试试. 如果删完了你可能还得回首页再创建几个页面 ! 在结束之前再作一点最后的整理. 你可能已经注意到 {{{list.myt}}} 和 {{{titles.myt}}} 使用了相同的代码来生成列表. 出于对保持代码最小冗余的兴趣, 我们修改下 {{{templates/titles.myt}}}, 把下面的代码: {{{ |
Line 585: | Line 613: |
With this code: .. code-block:: HyperText |
}}} 替换为: {{{ |
Line 591: | Line 619: |
That's it! A working, production-ready wiki in 20 mins. You can visit http://127.0.0.1:5000/ once more to admire your work. |
}}} 就这样! 20分钟搞定了一个可以工作的, 可以投入正式使用 (working, production-ready) 的 wiki . 你可以再访问 http://127.0.0.1:5000/ 一次, 好好欣赏一下自己的作品 :) |
Line 596: | Line 625: |
After all that hard work it would be good to distribute the finished package wouldn't it? Luckily this is really easy in Pylons too. In the project root directory run this command:: |
经历了所以这些困难的动作, 要是能分发完成了的包多好. 幸运的是在 Pylons 中这也是相当简单的. 在项目根目录下运行以下命令: {{{ |
Line 599: | Line 629: |
This will create an egg file in ``dist`` which contains everything anyone needs to run your program. They can install it with:: |
}}} 它会在 {{{dist}}} 目录下创建一个 egg 文件, 该目录还包含所有运行你的程序所必须的的东西. 用户可以这样安装这个包: {{{ |
Line 603: | Line 635: |
You should probably make eggs for each version of Python your users might require by running the above command with both Python 2.3 and 2.4. If you want to register your project with the Cheeseshop at http://www.python.org/pypi you can run the command below. *Please only do this with your own projects though because QuickWiki has already been registered!* :: |
}}} 你很可能应该为每一个你的用户可能使用的 Python 版本制作 egg, 只要在 Python 2.3 和 2.4 下分别运行上面这个命令就行了. 如果你想要将你的项目注册到位于 http://www.python.org/pypi 的 Cheeseshop , 你可以运行下面这条命令. *Please only do this with your own projects though because QuickWiki has already been registered!* {{{ |
Line 611: | Line 645: |
.. Warning:: The CheeseShop authentication is very weak and passwords are transmitted in plain text. Don't use any sign in details that you use for important applications as they could be easily intercepted. You will be asked a number of questions and then the information you entered in ``setup.py`` will be used as a basis for the page that is created. Now visit http://www.python.org/pypi to see the new index with your new package listed. .. Note:: A `CheeseShop Tutorial <http://wiki.python.org/moin/CheeseShopTutorial>`_ has been written and `full documentation on setup.py <http://docs.python.org/dist/dist.html>`_ is available from the Python website. You can even use `reStructuredText <http://docutils.sourceforge.net/rst.html>`_ in the ``description`` and ``long_description`` areas of ``setup.py`` to add formatting to the pages produced on the CheeseShop. There is also `another tutorial here <http://www.python.org/~jeremy/weblog/030924.html>`_. Finally you can sign in to the CheeseShop with the account details you used when you registered your application and upload the eggs you've created. If that seems too difficult you can even use this command which should be run for each version of Python supported to upload the eggs for you:: |
}}} * 警告: CheeseShop 的验证很脆弱, 密码直接以明文传输. 不要使用任何详细的签名, 因为它们很容易被截获. 你需要回答许多问题然后你在 {{{setup.py}}} 中输入的信息会用来作为创建的页面的基本信息. 现在访问 http://www.python.org/pypi 查看新的项目索引, 看到你的包了吧 :) * 注意: 有人写了一篇 [http://wiki.python.org/moin/CheeseShopTutorial CheeseShop Tutorial] 而且 [http://docs.python.org/dist/dist.html full documentation on setup.py] 也可以在 python 网站看到. 你甚至可以在 {{{setup.py}}} 的 {{{description}}} 和 {{{long_description}}} 区域中使用 [http://docutils.sourceforge.net/rst.html reStructuredText] 来为在 CheeseShop 中创建的页面添加一点格式. 这里还要 [http://www.python.org/~jeremy/weblog/030924.html another tutorial here]. 最后当你注册你的应用程序并上传你创建的 eggs 的时候, 你可以用你的帐号详细资料登录到 CheeseShop . 如果这看起来太困难的话 你甚至可以使用这个命令, 它应该能在所有支持上传 eggs 的 python 版本上使用: {{{ |
Line 623: | Line 659: |
Before this will work you will need to create a ``.pypirc`` file in your home directory containing your username and password so that the ``upload`` command knows who to sign in as. It should look similar to this:: |
}}} 不过之前你得在 home 目录下先创建一个 {{{.pypirc}}} 文件, 它包含你的用户名和密码, 这样 {{{upload}}} 命令才知道是谁登录. 这个文件应该大概是这个样子: {{{ |
Line 629: | Line 667: |
.. Tip:: This works on windows too but you will need to set your ``HOME`` environment variable first. If your home directory is ``C:\Documents and Settings\James`` you would put your ``.pypirc`` file in that directory and set your ``HOME`` environment variable with this command:: |
}}} * 技巧: 这个命令也可以在 windows 下使用不过你需要设置你的 {{{HOME}}} 环境变量先. 如果你的 home 目录是 {{{C:\Documents and Settings\James}}} 你就将你的 {{{.pypirc}}} 文件放在那个目录下然后使用这个命令设置你的 {{{HOME}}} 环境变量: {{{ |
Line 633: | Line 673: |
You can now use the ``python setup.py bdist_egg upload`` as normal. Now that the application is on CheeseShop anyone can install it with the ``easy_install`` command exactly as we did right at the very start of this tutorial. |
}}} 现在你可以正常使用 {{{python setup.py bdist_egg upload}}} 了 现在这个应用程序在 CheeseShop 上了, 现在任何人都可以像我们在本教程最开始的时候做的那样一样使用 {{{easy_install}}} 命令来安装了. |
Line 640: | Line 681: |
A final word about security. .. Danger:: Always set ``debug = false`` in configuration files for production sites and make sure your users do to. You should NEVER run a production site accessible to the public with debug mode on. If there was a problem with your application and an interactive error page was shown, the visitor would be able to run any Python commands they liked in the same way you can when you are debugging. This would obviously allow them to do all sorts of malicious things so it is very important you turn off interactive debugging for production sites by setting ``debug = false`` in configuration files and also that you make users of your software do the same. |
最后再说几句关于安全的话. * 危险: 总是在产品站点的配置文件中设置 {{{debug = false}}} 并保证你的用户也这么做. 你永远都不应该以 debug 模式运行一个可以公开访问的产品站点. 如果程序出现问题, 会显示一个交互的错误页面, 访问者能够运行任何 python 命令, 就和你在调试时一样. 这显然让他们能够做任何类型的恶意的事情, 所以在所有产品站点的配置文件设置 {{{debug = false}}} 关闭交互式调试是非常重要的, 同样要保证使用你的软件的用户也这么做. |
Line 648: | Line 689: |
We've gone through the whole cycle of creating and distributing a Pylons application looking at setup and configuration, routing, models, controllers and templates. Hopefully you have an idea of how powerful Pylons is and, once you get used to the concepts introduced in this tutorial, how easy it is to create sophisticated, distributable applications with Pylons. That's it, I hope you found the tutorial useful. You are encouraged to email any comments to the `Pylons mailing list <http://groups.google.co.uk/group/pylons-discuss>`_ where they will be gratefully received. |
我们已经经历从创建到分发一个 Pylons 应用程序的整个周期 (setup and configuration, routing, models, controllers, 和 templates) 希望你已经对 Pylons 的强大已经有所认识了, 并且一旦你习惯了本教程介绍的这些观念, 你会发现用 Pylons 创建复杂的,可分发的应用程序是多么的容易 ! 就这样了, 希望本教程对你有用. 期待你将你的评论email到 [http://groups.google.co.uk/group/pylons-discuss Pylons mailing list], 他们会很高兴收到你的评论 :) |
Line 653: | Line 694: |
== 讨论 == * 有什么想说的就放在这里吧 :) |
QuickWiki Tutorial [ for pylons ]
- 作者: James Gardner
- 翻译: ["huangyi"]
简介
请先阅读 [http://www.pylonshq.com/docs/0.9.1/install.html installation instructions] 和 [http://www.pylonshq.com/docs/0.9.1/getting_started.html getting started] 两份指南 , 如果你还没读过的话
在这个教程中我们要使用 Pylons0.9 和 SQLAlchemy 从头创建一个可以工作的 wiki. 我们的 wiki 可以允许用户添加,修改,删除带有格式的wiki页面.
从成品开始 Starting at the End
Pylons 的设计目标是要方便所有人, 而不仅仅是开发者, 所有让我们以 QuickWiki 最终用户的身份下载并安装一个 QuickWiki 的成品先. 等我们发掘完它的特性后, 我们就从头开始编写它.
先安装 [http://peak.telecommunity.com/DevCenter/EasyInstall Easy Install] , 然后运行以下命令来安装 QuickWiki 并创建一个配置文件:
> easy_install QuickWiki > paster make-config QuickWiki test.ini
下面编辑该配置文件, 在 [app:main] 节中指定 dsn 变量, 让数据源的名称指向你希望使用的数据库.
注意: 默认的数据源名称 dsn = postgres://@localhost/quickwiki_test 使用一个叫 quickwiki_test 的在本地运行的 PostgreSQL 数据库. 你可以使用 PostgreSQL 的命令 createdb quickwiki_test 来创建这个数据库, 你也可以使用 MySQL, Oracle 或者 SQLite. Firebird 和 MS-SQL 应该也能行. 连接不同数据库的详细信息请查看 [http://www.sqlalchemy.org/docs/dbengine.myt#dbengine_establishing SQLAlchemy documentation]. 比如 SQLite 的 dsn 里需要有4个反斜线. 你还得确定你已经有了你要使用的数据库的相应的 python驱动. 比如用 PostgreSQL 的话你就需要 [http://www.initd.org/projects/psycopg1 psycopg]
最后, 创建数据表并启动该成品:
> paster setup-app test.ini > paster serve test.ini
这样就OK了 ! 现在你可以访问 http://127.0.0.1:5000 并实验这个 Wiki 成品. 注意, 在标题列表页, 只要将页面标题拖到垃圾区域, 就可以通过 Ajax 调用删除它.
弄完这些以后, 使用 CTRL+C 停止服务器, 因为我们要开始开发我们自己的版本了.
开发 QuickWiki
如果你跳过了上面这一节那你需要安装 Pylons 0.9 先:
> easy_install -U Pylons[full]==0.9
然后创建你的 project :
> paster create --template=pylons QuickWiki
我们启动服务器看看现在有了点啥:
> cd QuickWiki > paster serve --reload development.ini
注意: 我们使用了--reload选项启动服务器. 这意味着这以后我们对代码做的任何更改都会导致服务器重启(如果有必要重启的话); 你的改变可以实时得在当前站点上反映出来.
开一个新的控制台并执行 cd QuickWiki/quickwiki . 访问 http://127.0.0.1:5000 你就可以看到介绍页面. 删除文件 public/index.html ,因为我们想看到 wiki 的首页而不是欢迎页面. 如果你现在就刷新页面的话, Pylons 内置的错误文档会起作用并显示一个 Error 404 的页面, 告诉你该文件找不到. 待会我们会建立一个 controller 来处理对这个地址的访问.
Model
Pylons 使用的是一个 Model View Controller 的架构; 我们从创建 model 开始. 我们可以使用任何我们喜欢的系统来作为我们的 model 层, 包括 [http://www.sqlobject.org SQLObject] 或者 [http://www.sqlalchemy.org SQLAlchemy]. 下面我们将使用 SQLAlchemy 来开发 QuickWiki.
注意: SQLAlchemy 是一个 Python SQL工具包 和 Object Relational Mapper , 它正迅速成为许多 Pylons 开发者的默认选择. 它提供了一套完整的众所周知的企业级持久层模式的实现, 设计目标是高效和高性能的数据库访问, 并向外提供一个简单的Pythonic的领域语言 (adapted into a simple and Pythonic domain language). 在 SQLAlchemy 的网站 http://sqlalchemy.org/docs/ 上有完整的详细的文档, 强烈建议在你大量使用 SQLAlchemy 之前看看这些文档先.
将 models/__init__.py 编辑成下面这样:
前面两行将 SQLAlchemy 设置为支持线程, 并导入一些有用的对象, 比如 Table 和 Column. 第二行 meta = DynamicMetaData() 是让我们可以在以后将数据表绑定到特定的数据库上去. 然后我们定义了一个叫做 pages 的表, 它有两列: title 和 content.
注意: SQLAlchemy 也支持直接从数据库表中反射这些信息, 如果我们已经创建好了数据库的话,SQLAlchemy 可以自动构造 Table 对象.
SQLAlchemy 一个核心的哲学就是 数据表 和 领域类 是两个不同的东西. 下面, 我们就要创建一个 Python 类, 它表示 wiki 中的页面,并且会通过一个 mapper 将一个领域对象映射为 pages 表中的一行. 将下面这些代码添加到 models/__init__.py 的底部:
稍微想远一点, 我们的 wiki 将会需要一些格式, 所以我们需要将 content 字段转换成 HTML. 任何 WikiWords (就是那些由一个大写字母和几个小写字母组成的单词连接起来组成的 which are words made by joining together two or more lowercase words with the first letter capitalized) 也需要转换成超链接.
要是我们可以给我们的 Page 对象添加一个方法,返回格式化好的 HTML 该多好啊. 将以下代码加到 models/__init__.py 的头部:
然后给Page对象添加一个 get_wiki_content() 方法, 像这样:
1 class Page(object):
2 def __str__(self):
3 return self.title
4
5 content = None
6
7 def get_wiki_content(self):
8 content = publish_parts(self.content, writer_name="html")["html_body"]
9 titles = wikiwords.findall(content)
10 if titles:
11 for title in titles:
12 content = content.replace(
13 title,
14 h.link_to(
15 title,
16 h.url_for(
17 controller='page',
18 action='index',
19 title=title
20 ),
21 ),
22 )
23 return content
这些代码值得解释一下.content = None这一行是在创建一个新的Page对象的时候将属性content初始化为None. 如果Page对象表示pages表中的一行,那么self.content就是字段content的值. h.link_to() 和 h.url_for() 是标准的 Pylons helpers, 这两个函数创建指向特定controller actions的链接. 现在我们已经决定所有 WikiWords 应该链接到 page controller 的 index action , 这个 controller 我们会在后面创建.
最后再做一点修改, 既然我们使用到了 docutils 和 SQLAlchemy 两个第三方的库, 那么我们还需要修改下我们的 setup.py 文件, 这样当别人通过[http://peak.telecommunity.com/DevCenter/EasyInstall Easy Install] 安装 QuickWiki 的时候就会自动安装依赖的这些库. 修改项目根目录下的setup.py, 将其中 install_requires 修改为如下:
1 install_requires=["Pylons>=0.9", "docutils==0.4", "SQLAlchemy>=0.2.6"],
既然我们正在修改 setup.py ,干脆把其他一些修改也一起做了吧. 将版本号设置为 0.1.1, 并添加一个描述和URL, 当我们把它发布在Python Cheeseshop 上的时候会用得上:
我们可能也希望制作一个完整的发布版而不是一个开发版, 这样的话我们就把 setup.cfg 里的下面几行移除:
[egg_info] tag_build = dev tag_svn_revision = true
要想测试下依赖库的自动安装的话, 运行下面的命令, 他们会自动安装 docutils 和 SQLAlchemy ,如果你还没有装过的话:
> python setup.py develop
注意: 使用命令 python setup.py develop 安装该应用程序, 和最终用户通过egg文件安装的情形是一样的. 当你开发应用程序的时候这是很有用的,因为你不用每次修改了程序后想测试一下的时候都必须另外创建一个egg并安装才行.
连接数据库
你的项目中所有的 controllers 默认都继承自 lib/base.py 文件中的 BaseController. 我们要利用它来给所有 controllers 加上对 SQLAlchemy 的支持. 将下面这些代码加在 lib/base.py 的 import 语句后面,覆盖已有的 BaseController 类:
1 import sqlalchemy.mods.threadlocal
2 from sqlalchemy import *
3
4 class BaseController(WSGIController):
5 def __call__(self, environ, start_response):
6 model.meta.connect(
7 request.environ['paste.config']['app_conf']['dsn']
8 )
9 objectstore.clear()
10 response = WSGIController.__call__(self, environ, start_response)
11 objectstore.flush()
12 return response
现在每次用户请求, SQLAlchemy的对象都会连接到数据库了.
配置 安装
现在让我们再做一点修改, 目的是让最终用户能够将 QuickWiki 安装起来. 将 websetup.py ( 命令 paster setup-app 将会使用该函数 ) 修改为下面这样:
1 import sqlalchemy.mods.threadlocal
2 from sqlalchemy import *
3 from quickwiki.models import *
4 from paste.deploy import appconfig
5
6 def setup_config(command, filename, section, vars):
7 app_conf = appconfig('config:'+filename)
8 print "Connecting to database %s"%app_conf['dsn']
9 meta.connect(app_conf['dsn'])
10 print "Creating tables"
11 meta.create_all()
12 print "Adding front page data"
13 page = Page()
14 page.title = 'FrontPage'
15 page.content = 'Welcome to the QuickWiki front page.'
16 objectstore.flush()
17 print "Successfully setup."
from quickwiki.models import * 这一行导入我们的 page 表, meta.connect(app_conf['dsn']) 这一行使用 用户在配置文件中指定的 DSN 连接数据库. 最后 meta.create_all() 创建数据表. 数据表创建完成后,代码还为简单的 wiki 首页添加了点数据.
要测试上面这个功能的话, 你首先需要安装你的 QuickWiki (如果你还没这么做过的话), 目的是让 paster 找到我们正在开发的版本而不是我们在教程最开始的时候安装的版本:
> python setup.py develop
然后你就可以编辑你的 development.ini , 并添加你的数据库 DSN:
[app:main] use = egg:quickwiki dsn = postgres://@localhost/quickwiki_test
你还需要修改 QuickWiki.egg_info/paste_deploy_config.ini_tmpl 如下, 这样当用户运行命令 paster make-config时 , 生成的配置文件总是会有一个节提示他们输入他们自己的数据库的DSN , 就像我们在一开始安装 QuickWiki 成品时所做的一样:
[app:main] use = egg:quickwiki # Specify your own database connection here dsn = postgres://@localhost/quickwiki_test
然后你就可以运行命令 paster setup-app 来建立你的数据表,就和最终用户将会做的一样. 如果以前版本的程序已经创建过数据表的话, 记得要删除并重建数据库. (译注:如果没有修改过model的话,就不需要)
> paster setup-app development.ini
- 注意: 关于具体支持哪些 DSN 的信息请查看第一段中关于 SQLAlchemy 的 "注意" , 和包含在其中的指向 SQLAlchemy文档中关于DSN不同选项部分的链接.
Templates
- 注意: Pylons 默认使用 Myghty 模板语言, 但是只要你愿意你可以方便地使用其他的模板语言, 同时不影响大部分 Pylons 的功能. Pylons另外还支持 Kid 和 Cheetah
Note: See the SQLAlchemy note in the Starting at the End_ section for information on supported DSNs and a link to the SQLAlchemy documentation about the various options that can be included in them.
我们会在我们的项目中用到 Myghty 模板语言 的一个叫做继承的特性. 首先在 templates/autohandler 中添加基页面模板:
<html> <head> <title>QuickWiki</title> <% h.stylesheet_link_tag('/quick.css') %> </head> <body> <div class="content"> % m.call_next() <p class="footer"> Return to the <% h.link_to('FrontPage', h.url_for(action="index", title="FrontPage")) %> | <% h.link_to('Edit '+c.title, h.url_for(title=c.title, action='edit')) %> </p> </div> </body> </html>
所有其他的模板会自动地被插入到 % m.call_next() 这行, 并且当我们在控制器中调用 render_response() 的时候会返回整个页面, 这样我们就可以方便地给我们所有的模板应用统一的主题了.
如果你有兴趣学习 Myghty模板 的其他一些特性的话可以去看看易懂的 [http://www.myghty.org/docs/ Myghty 文档]. 现在我们只需要理解那一行会被子模板替换 和 任何在 <% 和 %> 之间的部分会被当作python程序执行然后被替换成执行结果. 在一行的第一个字符处写 % 等于是用 <% 和 %> 把整行扩起来.
这个 autohandler 用到几个被附在 h 对象上的 helper 函数. 这些函数在[http://pylonshq.com/WebHelpers/module-index.html WebHelpers 文档]中有详细描述. 你还可以向 h 上附加更多的helpers, 只需把他们写到 lib/helpers.py 中即可, 虽然这个项目中我们还不需要.
警告:如果在 % 字符前有空格的话, 你的模板将不起作用.
Routing (url dispatcher)
在我们添加任何 action 之前我们希望能够将请求正确的路由( route )到这些 action . 编辑 config/routing.py 文件, 把 route map 改成这样:
关于强大的将请求路由(route) 到 controllers 和 actions 的功能的完整文档在这里: [http://routes.groovie.org/docs/ Routes文档].
Controllers
简单回顾: 我们已经建立了 model, 配置好了应用程序, 添加了 routes 并在 autohandler 中设置了基模板, 现在我们需要编写项目了! 在你的项目的根目录下使用下面这个命令添加一个叫做page的 controller :
> paster controller page
我们需要下面几个 actions:
1 index(self, title)
2 displays a page based on the title
3
4 edit(self, title)
5 displays a from for editing the page title
6
7 save(self, title)
8 save the page title and show it with a saved message
9
10 list(self)
11 gives a list of all pages
12
13 delete(self)
14 deletes a page based on an AJAX drag and drop call
Let's get cracking! 在 page controller controller/page.py 的顶端紧靠现有 import 语句下面的地方添加以下几个 import 语句:
将这个方法添加到 PageController 类, 在每个请求到来之时为 Page 创建一个 query 对象:
注意: __before__() 是一个特殊的 action, 每次请求到来地时候, 在调用实际 action 之前都会先调用 __defore__}}, 还有一个 {{{__after__ 则是在调用实际 action 之后调用.
index()
用下面代码替换 index() action:
添加一个叫 templates/page.myt 的模板,内容如下:
<h1 class="main"><% c.title %></h1> <% c.content %>
这个模板只是简单地显示页面的 title 和 content.
注意: Pylons 自动把所有 action 参数赋给 context object c, 这样你就不用亲自动手了. 这样, title 的值自动赋给 c.title , 可以直接在模板中使用了. 我们还得在 controller 中手动给 c.content 赋值 (译注: 对象 c 可以直接在模板中使用, 用来向模板中传递数据)
我们还需要为页面不存在的情况编写一个模板. 需要在上面显示一条信息和一个到 edit action 的链接, 这样该页面才可以被创建. 添加一个叫 templates/new_page.myt 的模板, 内容如下:
<h1 class="main"><% c.title %></h1> <p>This page doesn't exist yet. <a href="<% h.url_for(action='edit', title=c.title) %>">Create the page</a>.</p>
现在我们可以测试我们的 QuickWiki 了 , 看看现在怎么样了. 如果你还没有把服务器运行起来的话,使用下面命令运行它:
> paster serve --reload development.ini
访问 http://127.0.0.1:5000/ 你就可以看到 wiki 的首页了. 我们可以添加一个已经在 templates/autohandler 中链进来了的 css 把它变漂亮一点点. 把以下内容加到文件 public/quick.css 中:
body { background-color: #888; margin: 25px; } div.content{ margin: 0; margin-bottom: 10px; background-color: #d3e0ea; border: 5px solid #333; padding: 5px 25px 25px 25px; } h1.main{ width: 100%; border-bottom: 1px solid #000; } p.footer{ width: 100%; padding-top: 3px; border-top: 1px solid #000; }
刷新页面, 这时我们便有了一个更漂亮的 wiki 了. 你会注意到单词 QuickWiki 已经被 Page 领域对象的 get_wiki_content() 方法转换成一个超链接了. 你可以点击该链接然后会看到一个来自 new_page.myt 模板的新建页面的页面. 如果你点击 Create the page 链接你会看到 Pylons 的错误处理起作用并提示你 Action edit is not implemented. Well, 下面我们最好把它加上, 不过加它之前, 我们先玩一玩这个 [http://www.pylonshq.com/docs/0.9.1/interactive_debugger.html interactive debugger], 试着点击 + 或者 >> 箭头 , 你就可以交互式地调试你的应用程序, 这是一个相当有用的工具.
edit()
要编辑 wiki 页面, 我们需要先从数据库中取得没被转成HTML的内容, 并把它显示在一个简单的 form 里面等候编辑. 添加 edit() action:
并创建 templates/edit.myt 文件:
<h1 class="main">Editing <% c.title %></h1> <% h.start_form(h.url_for(action='save', title=c.title), method="get") %> <% h.text_area(name='content', rows=7, cols=40, content=c.content)%> <br /> <% h.submit(value="Save changes", name='commit') %> <% h.end_form() %>
注意: 你可能已经注意到我们只在页面存在的情况下设置 c.content, 但是在页面不存在的情况下 c.content 被 h.text_area 访问也没有抛出 AttributeError 异常. 我们其实是利用了这么一个规定: 对象 c 任何被访问的属性总是默认返回空字符串 "" . 这是对象 c 一个很有用的特性, 不过如果你期望的不是这种行为的话可能会让你很郁闷( catch you out )
我们使用到 h 对象来创建我们的 form 和 field 对象. 这个节省了一点点手工编写 HTML 的时间. 该 form 提交到 save() action 以保存刚刚新建的或更新过的内容, 下面我们来编写 save() .
save()
save() action 需要做的第一件事就是要检查要保存的页面是否已经存在. 如果不存在则使用 page = model.Page() 创建新页面. 然后再更新其内容. 在 Pylons 中你可以从 form 提交 中获取请求参数, 从相应名字的 request.params 对象中可以获取 GET 或者 POST 请求.
添加 save() action:
1 def save(self, title):
2 page = self.query.get_by(title=title)
3 if not page:
4 page = model.Page()
5 page.title = title
6 page.content = request.params['content']
7 c.title = page.title
8 c.content = page.get_wiki_content()
9 c.message = 'Successfully saved'
10 return render_response('/page.myt')
注意: request.params 是一个 MultiDict 对象; 一个排好序了的字典, 其中一个 key 可以对应多个值. 使用正常的 dict 访问方法 (params[key], params.get() 和 params.setdefault()) 访问 MultiDict 可以获取已存在的 key 对应的多个 value 中的一个, 如果想得到多个 value, 使用 getall() 方法, 它会通过一个列表中返回所有 value.
为了让 page.myt 模板在页面保存以后显示 Successfully saved 信息, 我们需要修改 templates/page.myt 文件. 在 <h1 class="main"><% c.title %></h1> 后面添加下面几行, 确保字符 % 前面没有空白:
% if c.message: <p><% c.message %> </p> % #end if
现在我们有了一个功能完整的 wiki , 你可以创建并编辑页面, 最终用户还可以通过简单的命令进行安装部署.
访问 http://127.0.0.1:5000 试试看.
要是有一个标题列表, 可以在里面删除页面该多好啊, 下面我们就来增加这个功能 !
list()
添加 list() action:
list() action 简单地从数据库中获取所有页面. 我们创建 templates/titles.myt 文件来显示该列表:
<h1 class="main">Title List</h1> <ul id="titles"> % for title in c.titles: <li> <% title %> [<% h.link_to('visit', h.url_for(title=title, action="index")) %>] </li> % #end for </ul>
最后编辑 templates/autohandler , 添加一个到标题列表页的链接, 这样页面底部就像这样:
<p class="footer"> Return to the <% h.link_to('FrontPage', h.url_for(action="index", title="FrontPage")) %> % if h.url_for() != '/page/list': | <% h.link_to('Edit '+c.title, h.url_for(title=c.title, action='edit')) %> | <% h.link_to('Title List', h.url_for(action='list', title=None)) %> % # end if </p>
如果你访问 http://127.0.0.1:5000/page/list 你应该可以看到完整的标题列表并且你应该可以访问到每个页面.
delete()
既然设计这个教程的目的是让你熟悉尽可能多的 Pylons核心功能, 那我们要使用一些 AJAX 来允许用户从标题列表拖动一个标题到垃圾区, 并自动删除该 wiki 页.
把下面这行添加到 templates/autohandler 中 </head> 的前面:
<% h.javascript_include_tag('/javascripts/effects.js', builtins=True) %>
注意: h.javascript_include_tag() helper 会创建指向所有我们需要的内置 JavaScript 的 link, 另外再添加 /javascripts/effects.js , 生成的 HTML 是这个样子:
<script src="/javascripts/prototype.js" type="text/javascript"></script> <script src="/javascripts/scriptaculous.js" type="text/javascript"></script> <script src="/javascripts/effects.js" type="text/javascript"></script>
如果你查看 config/middleware.py , 你会看到这么几行:
WSGI 应用程序 javascripts_app 将所有对 /javascripts/ 的请求直接映射为 WebHelpers 包中对应的 JavaScript.这意味这你不需要手动将 Pylons JavaScript 文件复制到你的项目, 并且如果你升级 Pylons , 你自动便可以使用最新的 scripts.
现在我们来编写 AJAX ! 我们想要标题列表中所有的标题是可拖动的, 我们把它们每一个都用一个 id 唯一 <span> 元素包起来. 编辑 templates/titles.myt 把这一行:
<% title %> [<% h.link_to('visit', h.url_for(title=title, action="index")) %>]
换成
<span id="page-<% title %>"><% title %></span> [<% h.link_to('visit', h.url_for(title=title, action="index")) %>] <% h.draggable_element("page-"+ str(title), revert=True) %>
该代码将每一个标题变成可拖动的元素, 如果没有拖到拖曳目标的话, 会自动回到原始位置. 如果想要删除页面的话, 我们最好还是添加一个拖曳目标. 现在你已经可以访问 http://127.0.0.1:5000/page/list 拖页面标题玩. 你应该能感受到这一个 helper 函数: h.draggable_element(), 提供了多少功能 !
我们最好准备个拖曳目标, 把标题拖到那里就自动删掉相应页面, 在 templates/titles.myt 中 <ul id="titles"> 这一行前面加上:
<div id="trash"> Delete a page by dragging its title here </div> <% h.drop_receiving_element("trash", update="titles", url=h.url_for(action="delete")) %>
我们还需要为垃圾箱添加 css 样式, 在 public/quick.css 的末尾加上:
div#trash{ float: right; margin: 0px 20px 20px 20px; background: #eee; border: 1px solid #000; padding: 15px; }
- 技巧: 有的时候调试 AJAX 应用程序是很困难的. Pylons 可以帮你. 在 debug 模式下如果程序发生了错误, 一个包含 interactive debugger 的 debug URL 会被打印到错误流中, 甚至在 AJAX 请求中也是如此. 只要复制粘贴到浏览器地址栏, 你就可以调试该请求了.
当一个标题被拖到垃圾箱后, 会发一个 AJAX 请求到 delete() action, 并通过参数 id 提交该拖动元素的id. 而 id 为 titles 的元素会被从 action 返回的东西替换, 所以我们最好再添加一个 delete() action, 它返回删除的页面后新的标题列表.
从 id 参数中获取页面标题, 然后加载, 然后删除. 对数据库的修改通过调用 objectstore.flush() 应用到数据库. 然后剩下的标题被渲染到模板 templates/list.myt , 该模板使用以下内容创建:
% for title in c.titles: <li> <span id="page-<% title %>"><% title %></span> [<% h.link_to('visit', h.url_for(title=title, action="index")) %>] <% h.draggable_element("page-"+ str(title), revert=True) %> </li> % #end for
注意: 我们给 render_response() 全局函数传递了 fragment=True . 它会把模板当作一个片段处理, 不会将输出插入到 autohandler. 如果我们没有用这个参数的话, 整个页面都会被返回并放在标题列表的位置, 这显然不是我们想要的结果.
访问 http://127.0.0.1:5000/page/list 然后删几个页面试试. 如果删完了你可能还得回首页再创建几个页面 !
在结束之前再作一点最后的整理. 你可能已经注意到 list.myt 和 titles.myt 使用了相同的代码来生成列表. 出于对保持代码最小冗余的兴趣, 我们修改下 templates/titles.myt, 把下面的代码:
% for title in c.titles: <li> <span id="page-<% title %>"><% title %></span> [<% h.link_to('visit', h.url_for(title=title, action="index")) %>] <% h.draggable_element("page-"+ str(title), revert=True) %> </li> % #end for
替换为:
<% render('/list.myt', fragment=True) %>
就这样! 20分钟搞定了一个可以工作的, 可以投入正式使用 (working, production-ready) 的 wiki . 你可以再访问 http://127.0.0.1:5000/ 一次, 好好欣赏一下自己的作品
发布该产品
经历了所以这些困难的动作, 要是能分发完成了的包多好. 幸运的是在 Pylons 中这也是相当简单的. 在项目根目录下运行以下命令:
> python setup.py bdist_egg
它会在 dist 目录下创建一个 egg 文件, 该目录还包含所有运行你的程序所必须的的东西. 用户可以这样安装这个包:
> easy_install QuickWiki-0.1.1-py2.4.egg
你很可能应该为每一个你的用户可能使用的 Python 版本制作 egg, 只要在 Python 2.3 和 2.4 下分别运行上面这个命令就行了.
如果你想要将你的项目注册到位于 http://www.python.org/pypi 的 Cheeseshop , 你可以运行下面这条命令.
*Please only do this with your own projects though because QuickWiki has already been registered!*
> python setup.py register
警告: CheeseShop 的验证很脆弱, 密码直接以明文传输. 不要使用任何详细的签名, 因为它们很容易被截获.
你需要回答许多问题然后你在 setup.py 中输入的信息会用来作为创建的页面的基本信息.
现在访问 http://www.python.org/pypi 查看新的项目索引, 看到你的包了吧
注意: 有人写了一篇 [http://wiki.python.org/moin/CheeseShopTutorial CheeseShop Tutorial] 而且 [http://docs.python.org/dist/dist.html full documentation on setup.py] 也可以在 python 网站看到. 你甚至可以在 setup.py 的 description 和 long_description 区域中使用 [http://docutils.sourceforge.net/rst.html reStructuredText] 来为在 CheeseShop 中创建的页面添加一点格式. 这里还要 [http://www.python.org/~jeremy/weblog/030924.html another tutorial here].
最后当你注册你的应用程序并上传你创建的 eggs 的时候, 你可以用你的帐号详细资料登录到 CheeseShop . 如果这看起来太困难的话 你甚至可以使用这个命令, 它应该能在所有支持上传 eggs 的 python 版本上使用:
> python setup.py bdist_egg upload
不过之前你得在 home 目录下先创建一个 .pypirc 文件, 它包含你的用户名和密码, 这样 upload 命令才知道是谁登录. 这个文件应该大概是这个样子:
[server-login] username:james password:password
技巧: 这个命令也可以在 windows 下使用不过你需要设置你的 HOME 环境变量先. 如果你的 home 目录是 C:\Documents and Settings\James 你就将你的 .pypirc 文件放在那个目录下然后使用这个命令设置你的 HOME 环境变量:
> SET HOME=C:\Documents and Settings\James
现在你可以正常使用 python setup.py bdist_egg upload 了
现在这个应用程序在 CheeseShop 上了, 现在任何人都可以像我们在本教程最开始的时候做的那样一样使用 easy_install 命令来安装了.
安全
最后再说几句关于安全的话.
危险: 总是在产品站点的配置文件中设置 debug = false 并保证你的用户也这么做.
你永远都不应该以 debug 模式运行一个可以公开访问的产品站点. 如果程序出现问题, 会显示一个交互的错误页面, 访问者能够运行任何 python 命令, 就和你在调试时一样. 这显然让他们能够做任何类型的恶意的事情, 所以在所有产品站点的配置文件设置 debug = false 关闭交互式调试是非常重要的, 同样要保证使用你的软件的用户也这么做.
总结
我们已经经历从创建到分发一个 Pylons 应用程序的整个周期 (setup and configuration, routing, models, controllers, 和 templates) 希望你已经对 Pylons 的强大已经有所认识了, 并且一旦你习惯了本教程介绍的这些观念, 你会发现用 Pylons 创建复杂的,可分发的应用程序是多么的容易 !
就这样了, 希望本教程对你有用. 期待你将你的评论email到 [http://groups.google.co.uk/group/pylons-discuss Pylons mailing list], 他们会很高兴收到你的评论
James Gardner
讨论
有什么想说的就放在这里吧