Bottle 0.12-dev - en to zh translation catalogue (141 entries)

en: Tutorial: Todo-List Application
zh: Tutorial: Todo-List 应用

en: This tutorial is a work in progess and written by `noisefloor <http://github.com/noisefloor>`_.
zh: 这份教程是 `noisefloor <http://github.com/noisefloor>`_ 编写的，并在不断完善中。

en: This tutorial should give a brief introduction to the Bottle_ WSGI Framework. The main goal is to be able, after reading through this tutorial, to create a project using Bottle. Within this document, not all abilities will be shown, but at least the main and important ones like routing, utilizing the Bottle template abilities to format output and handling GET / POST parameters.
zh: 这份教程简单介绍了Bottle框架，目的是让你看完后能在项目中使用Bottle。它没有涵盖所有东西，但介绍了URL映射，模板，处理GET/POST请求等基础知识。

en: To understand the content here, it is not necessary to have a basic knowledge of WSGI, as Bottle tries to keep WSGI away from the user anyway. You should have a fair understanding of the Python_ programming language. Furthermore, the example used in the tutorial retrieves and stores data in a SQL databse, so a basic idea about SQL helps, but is not a must to understand the concepts of Bottle. Right here, SQLite_ is used. The output of Bottle sent to the browser is formatted in some examples by the help of HTML. Thus, a basic idea about the common HTML tags does help as well.
zh: 读懂这份教程，你不需要事先了解WSGI标准，Bottle也一直避免用户直接接触WSGI标准。但你需要了解 Python_ 这门语言。更进一步，这份教程中的例子需要从SQL数据库中读写数据，所以事先了解一点SQL知识是很有帮助的。例子中使用了 SQLite_ 来保存数据。因为是网页应用，所以事先了解一点HTML的知识也很有帮助。

en: For the sake of introducing Bottle, the Python code "in between" is kept short, in order to keep the focus. Also all code within the tutorial is working fine, but you may not necessarily use it "in the wild", e.g. on a public web server. In order to do so, you may add e.g. more error handling, protect the database with a password, test and escape the input etc.
zh: 作为一份教程，我们的代码尽可能做到了简明扼要。尽管教程中的代码能够工作，但是我们还是不建议你在公共服务器中使用教程中的代码。如果你想要这样做，你应该添加足够的错误处理，并且加密你的数据库，处理用户的输入。

en: Goals
zh: 目标

en: At the end of this tutorial, we will have a simple, web-based ToDo list. The list contains a text (with max 100 characters) and a status (0 for closed, 1 for open) for each item. Through the web-based user interface, open items can be view and edited and new items can be added.
zh: 在这份教程结束的时候，我们将完成一个简单的，基于Web的ToDo list(待办事项列表)。列表中的每一个待办事项都包含一条文本(最长100个字符)和一个状态(0表示关闭，1表示开启)。通过网页，已开启的待办事项可以被查看和编辑，可添加待办事项到列表中。

en: During development, all pages will be available on ``localhost`` only, but later on it will be shown how to adapt the application for a "real" server, including how to use with Apache's mod_wsgi.
zh: 在开发过程中，所有的页面都只可以通过 ``localhost`` 来访问，完了会介绍如何将应用部署到"真实"服务器的服务器上面，包括使用mod_wsgi来部署到Apache服务器上面。

en: Bottle will do the routing and format the output, with the help of templates. The items of the list will be stored inside a SQLite database. Reading and  writing the database will be done by Python code.
zh: Bottle会负责URL映射，通过模板来输出页面。待办事项列表被存储在一个SQLite数据库中，通过Python代码来读写数据库。

en: We will end up with an application with the following pages and functionality:
zh: 我们会完成以下页面和功能:

en: start page ``http://localhost:8080/todo``
zh: 首页 ``http://localhost:8080/todo``

en: adding new items to the list: ``http://localhost:8080/new``
zh: 添加待办事项: ``http://localhost:8080/new``

en: page for editing items: ``http://localhost:8080/edit/:no``
zh: 编辑待办事项: ``http://localhost:8080/edit/:no``

en: validating data assigned by dynamic routes with the @validate decorator
zh: 通过 @validate 修饰器来验证数据合法性

en: catching errors
zh: 捕获错误

en: Before We Start...
zh: 开始之前...

en: Install Bottle
zh: 安装Bottle

en: Assuming that you have a fairly new installation of Python (version 2.5 or higher), you only need to install Bottle in addition to that. Bottle has no other dependencies than Python itself.
zh: 假设你已经安装好了Python (2.5或更改版本)，接下来你只需要下载Bottle就行了。除了Python标准库，Bottle没有其他依赖。

en: You can either manually install Bottle or use Python's easy_install: ``easy_install bottle``
zh: 你可通过Python的esay_install命令来安装Bottle: ``easy_install bottle``

en: Further Software Necessities
zh: 其它软件

en: As we use SQLite3 as a database, make sure it is installed. On Linux systems, most distributions have SQLite3 installed by default. SQLite is available for Windows and MacOS X as well and the `sqlite3` module is part of the python standard library.
zh: 因为我们使用SQLite3来做数据库，请确保它已安装。如果是Linux系统，大多数的发行版已经默认安装了SQLite3。SQLite同时可工作在Windows系统和MacOS X系统上面。Pyhton标准库中，已经包含了 `sqlite3` 模块。

en: Create An SQL Database
zh: 创建一个SQL数据库

en: First, we need to create the database we use later on. To do so, save the following script in your project directory and run it with python. You can use the interactive interpreter too::
zh: 首先，我们需要先创建一个数据库，稍后会用到。在你的项目文件夹执行以下脚本即可，你也可以在Python解释器逐条执行。

en: This generates a database-file `todo.db` with tables called ``todo`` and three columns ``id``, ``task``, and ``status``. ``id`` is a unique id for each row, which is used later on to reference the rows. The column ``task`` holds the text which describes the task, it can be max 100 characters long. Finally, the column ``status`` is used to mark a task as open (value 1) or closed (value 0).
zh: 现在，我们已经创建了一个名字为 `todo.db` 的数据库文件，数据库中有一张名为 ``todo`` 的表，表中有 ``id`` , ``task`` , 及 ``status`` 这三列。每一行的 ``id`` 都是唯一的，稍后会根据id来获取数据。 ``task`` 用于保存待办事项的文本，最大长度为100个字符。最后 ``status`` 用于标明待办事项的状态，0为开启，1为关闭。

en: Using Bottle for a Web-Based ToDo List
zh: 基于Bottle的待办事项列表

en: Now it is time to introduce Bottle in order to create a web-based application. But first, we need to look into a basic concept of Bottle: routes.
zh: 为了创建我们的Web应用，我们先来介绍一下Bottle框架。首先，我们需要了解Bottle中的route，即URL映射。

en: Understanding routes
zh: route URL映射

en: Basically, each page visible in the browser is dynamically generated when the page address is called. Thus, there is no static content. That is exactly what is called a "route" within Bottle: a certain address on the server. So, for example, when the page ``http://localhost:8080/todo`` is called from the browser, Bottle "grabs" the call and checks if there is any (Python) function defined for the route "todo". If so, Bottle will execute the corresponding Python code and return its result.
zh: 基本上，浏览器访问的每一页面都是动态生成的。Bottle通过route，将浏览器访问的URL映射到具体的Python函数。例如，在我们访问  ``http://localhost:8080/todo`` 的时候，Bottle会查找 ``todo`` 这个route映射到了哪个函数上面，接着调用该函数来响应浏览器请求。

en: First Step - Showing All Open Items
zh: 第一步 - 显示所有已开启的待办事项

en: So, after understanding the concept of routes, let's create the first one. The goal is to see all open items from the ToDo list::
zh: 在我们了解什么是route后，让我们来试着写一个。访问它即可查看所有已开启的待办事项 ::

en: Save the code a ``todo.py``, preferably in the same directory as the file ``todo.db``. Otherwise, you need to add the path to ``todo.db`` in the ``sqlite3.connect()`` statement.
zh: 将上面的代码保存为 ``todo.py`` ，放到 ``todo.db`` 文件所在的目录。如果你想将它们分开放，则需要在 ``sqlite3.connect()`` 函数中写上 ``todo.db`` 文件的路径。

en: Let's have a look what we just did: We imported the necessary module ``sqlite3`` to access to SQLite database and from Bottle we imported ``route`` and ``run``. The ``run()`` statement simply starts the web server included in Bottle. By default, the web server serves the pages on localhost and port 8080. Furthermore, we imported ``route``, which is the function responsible for Bottle's routing. As you can see, we defined one function, ``todo_list()``, with a few lines of code reading from the database. The important point is the `decorator statement`_ ``@route('/todo')`` right before the ``def todo_list()`` statement. By doing this, we bind this function to the route ``/todo``, so every time the browsers calls ``http://localhost:8080/todo``, Bottle returns the result of the function ``todo_list()``. That is how routing within bottle works.
zh: 来看看我们写的代码。导入了必须的 ``sqlite3`` 模块，从Bottle中导入 ``route`` 和 ``run`` 。``run()`` 函数启动了Bottle的内置开发服务器，默认情况下，开发服务器在监听本地的8080端口。``route`` 是Bottle实现URL映射功能的修饰器。你可以看到，我们定义了一个 ``todo_list()`` 函数，读取了数据库中的数据。然后我们使用 ``@route('/todo')`` 来将 ``todo_list()`` 函数和``todo`` 这个route绑定在一起。每一次浏览器访问 ``http://localhost:8080/todo`` 的时候，Bottle都会调用 ``todo_list()`` 函数来响应请求，并返回页面，这就是route的工作方式了。

en: Actually you can bind more than one route to a function. So the following code::
zh: 事实上，你可以给一个函数添加多个route。

en: will work fine, too. What will not work is to bind one route to more than one function.
zh: 这样是正确的。但是反过来，你不能将一个route和多个函数绑定在一起。

en: What you will see in the browser is what is returned, thus the value given by the ``return`` statement. In this example, we need to convert ``result`` in to a string by ``str()``, as Bottle expects a string or a list of strings from the return statement. But here, the result of the database query is a list of tuples, which is the standard defined by the `Python DB API`_.
zh: 你在浏览器中看到的即是你在 ``todo_list()`` 函数中返回的页面。在这个例子中，我们通过 ``str()`` 函数将结果转换成字符串，因为Bottle期望函数的返回值是一个字符串或一个字符串的列表。但 `Python DB API`_ 中规定了，数据库查询的返回值是一个元组的列表。

en: Now, after understanding the little script above, it is time to execute it and watch the result yourself. Remember that on Linux- / Unix-based systems the file ``todo.py`` needs to be executable first. Then, just run ``python todo.py`` and call the page ``http://localhost:8080/todo`` in your browser. In case you made no mistake writing the script, the output should look like this::
zh: 现在，我们已经了解上面的代码是如何工作的，是时候运行它来看看效果了。记得在Linux或Unix系统中， ``todo.py`` 文件需要标记为可执行(译者注：没有必要)。然后，通过 ``python todo.py`` 命令来执行该脚本，接着用浏览器访问 ``http://localhost:8080/todo`` 来看看效果。如果代码没有写错，你应该会在页面看到以下输出 ::

en: If so - congratulations! You are now a successful user of Bottle. In case it did not work and you need to make some changes to the script, remember to stop Bottle serving the page, otherwise the revised version will not be loaded.
zh: 如果是这样，那么恭喜你！如果出现错误，那么你需要检查代码时候写错，修改完后记得重启HTTP服务器，要不新的版本不会生效。

en: Actually, the output is not really exciting nor nice to read. It is the raw result returned from the SQL query.
zh: 实际上，这个输出很难看，只是SQL查询的结果。

en: So, in the next step we format the output in a nicer way. But before we do that, we make our life easier.
zh: 所以，下一步我们会把它变得更好看。

en: Debugging and Auto-Reload
zh: 调试和自动加载

en: Maybe you already noticed that Bottle sends a short error message to the browser in case something within the script is wrong, e.g. the connection to the database is not working. For debugging purposes it is quite helpful to get more details. This can be easily achieved by adding the following statement to the script::
zh: 或许你已经注意到了，如果代码出错的话，Bottle会在页面上显示一个简短的错误信息。例如，连接数据库失败。为了方便调试， 我们希望错误信息更加具体，可加上以下语句。

en: By enabling "debug", you will get a full stacktrace of the Python interpreter, which usually contains useful information for finding bugs. Furthermore, templates (see below) are not cached, thus changes to templates will take effect without stopping the server.
zh: 开启调试模式后，出错时页面会打印出完整的Python运行栈。另外，在调试模式下，模板也不会被缓存，任何对模板的修改会马上生效，而不用重启服务器。

en: That ``debug(True)`` is supposed to be used for development only, it should *not* be used in production environments.
zh: ``debug(True)`` 是为开发时的调试服务的， *不应* 在生产环境中开启调试模式。

en: Another quite nice feature is auto-reloading, which is enabled by modifying the ``run()`` statement to
zh: 另外一个十分有用的功能是自动加载，可修改 ``run()`` 语句来开启。

en: This will automatically detect changes to the script and reload the new version once it is called again, without the need to stop and start the server.
zh: 这样会自动检测对脚本的修改，并自动重启服务器来使其生效。

en: Again, the feature is mainly supposed to be used while developing, not on production systems.
zh: 同上，这个功能并不建议在生产环境中使用。

en: Bottle Template To Format The Output
zh: 使用模板来格式化输出

en: Now let's have a look at casting the output of the script into a proper format.
zh: 现在我们试着格式化脚本的输出，使其更适合查看。

en: Actually Bottle expects to receive a string or a list of strings from a function and returns them by the help of the built-in server to the browser. Bottle does not bother about the content of the string itself, so it can be text formatted with HTML markup, too.
zh: 实际上，Bottle期望route的回调函数返回一个字符串或一个字符串列表，通过内置的HTTP服务器将其返回给浏览器。Bottle不关心字符串的内容，所以我们可以将其格式化成HTML格式。

en: Bottle brings its own easy-to-use template engine with it. Templates are stored as separate files having a ``.tpl`` extension. The template can be called then from within a function. Templates can contain any type of text (which will be most likely HTML-markup mixed with Python statements). Furthermore, templates can take arguments, e.g. the result set of a database query, which will be then formatted nicely within the template.
zh: Bottle内置了独创的模板引擎。模板是后缀名为 ``.tpl`` 的文本文件。模板的内容混合着HTML标签和Python语句，模板也可以接受参数。例如数据库的查询结果，我们可以在模板内将其漂亮地格式化。

en: Right here, we are going to cast the result of our query showing the open ToDo items into a simple table with two columns: the first column will contain the ID of the item, the second column the text. The result set is, as seen above, a list of tuples, each tuple contains one set of results.
zh: 接下来，我们要将数据库的查询结果格式化为一个两列的表格。表格的第一列为待办事项的ID，第二列为待办事项的内容。查询结果是一个元组的列表，列表中的每个元组后包含一个结果。

en: To include the template in our example, just add the following lines::
zh: 在例子中使用模板，只需要添加以下代码。

en: So we do here two things: first, we import ``template`` from Bottle in order to be able to use templates. Second, we assign the output of the template ``make_table`` to the variable ``output``, which is then returned. In addition to calling the template, we assign ``result``, which we received from the database query, to the variable ``rows``, which is later on used within the template. If necessary, you can assign more than one variable / value to a template.
zh: 我们添加了两样东西。首先我们从Bottle中导入了 ``template`` 函数以使用模板功能，接着，我们渲染 ``make_table`` 这个模板(参数是rows=result)，把模板函数的返回值赋予 ``output`` 变量，并返回 ``output`` 。如有必要，我们可添加更多的参数。

en: Templates always return a list of strings, thus there is no need to convert anything. Of course, we can save one line of code by writing ``return template('make_table', rows=result)``, which gives exactly the same result as above.
zh: 模板总是返回一个字符串的列表，所以我们无须转换任何东西。当然，我们可以将返回写为一行以减少代码量。

en: Now it is time to write the corresponding template, which looks like this::
zh: 对应的模板文件。

en: Save the code as ``make_table.tpl`` in the same directory where ``todo.py`` is stored.
zh: 将上面的代码保存为 ``make_table.tpl`` 文件，和 ``todo.py`` 放在同一个目录。

en: Let's have a look at the code: every line starting with % is interpreted as Python code. Please note that, of course, only valid Python statements are allowed, otherwise the template will raise an exception, just as any other Python code. The other lines are plain HTML markup.
zh: 看看上面的代码，以%开头的行被当作Python代码来执行。请注意，只有正确的Python语句才能通过编译，要不模板就会抛出一个异常。除了Python语句，其它都是普通的HTML标记。

en: As you can see, we use Python's ``for`` statement two times, in order to go through ``rows``. As seen above, ``rows`` is a variable which holds the result of the database query, so it is a list of tuples. The first ``for`` statement accesses the tuples within the list, the second one the items within the tuple, which are put each into a cell of the table. It is important that you close all ``for``, ``if``, ``while`` etc. statements with ``%end``, otherwise the output may not be what you expect.
zh: 如你所见，为了遍历 ``rows`` ，我们两次使用了Python的 ``for`` 语句。 ``rows``是持有查询结果的变量，一个元组的列表。第一个 ``for`` 语句遍历了列表中所有的元组，第二个 ``for`` 语句遍历了元组中的元素，将其放进表格中。 ``for`` , ``if`` , ``while`` 语句都需要通过 ``%end`` 来关闭，要不会得到不正确的结果。

en: If you need to access a variable within a non-Python code line inside the template, you need to put it into double curly braces. This tells the template to insert the actual value of the variable right in place.
zh: 如果想要在不以%开头的行中访问变量，则需要把它放在两个大括号中间。这告诉模板，需要用变量的实际值将其替换掉。

en: Run the script again and look at the output. Still not really nice, but at least more readable than the list of tuples. Of course, you can spice-up the very simple HTML markup above, e.g. by using in-line styles to get a better looking output.
zh: 再次运行这个脚本，页面输出依旧不是很好看，但是更具可读性了。当然，你可给模板中的HTML标签加上CSS样式，使其更好看。

en: Using GET and POST Values
zh: 使用GET和POST

en: As we can review all open items properly, we move to the next step, which is adding new items to the ToDo list. The new item should be received from a regular HTML-based form, which sends its data by the GET method.
zh: 能够查看所有代码事项后，让我们进入到下一步，添加新的待办事项到列表中。新的待办事项应该在一个常规的HTML表单中，通过GET方式提交。

en: To do so, we first add a new route to our script and tell the route that it should get GET data::
zh: 让我们先来添加一个接受GET请求的route。

en: To access GET (or POST) data, we need to import ``request`` from Bottle. To assign the actual data to a variable, we use the statement ``request.GET.get('task','').strip()`` statement, where ``task`` is the name of the GET data we want to access. That's all. If your GET data has more than one variable, multiple ``request.GET.get()`` statements can be used and assigned to other variables.
zh: 为了访问GET(或POST)中的数据，我们需要从Bottle中导入 ``request`` ，通过 ``request.GET.get('task', '').strip()`` 来获取表单中 ``task`` 字段的数据。可多次使用 ``request.GET.get()`` 来获取表单中所有字段的数据。

en: The rest of this piece of code is just processing of the gained data: writing to the database, retrieve the corresponding id from the database and generate the output.
zh: 接下来是对数据的操作：写入数据库，获取返回的ID，生成页面。

en: But where do we get the GET data from? Well, we can use a static HTML page holding the form. Or, what we do right now, is to use a template which is output when the route ``/new`` is called without GET data.
zh: 因为我们是从HTML表单中获取数据，所以现在让我们来创建这个表单吧。我们通过 ``/new`` 这个URL来添加待办事项。

en: The code needs to be extended to::
zh: 代码需要扩展如下::

en: ``new_task.tpl`` looks like this::
zh: 对应的 ``new_task.tpl`` 模板如下。

en: That's all. As you can see, the template is plain HTML this time.
zh: 如你所见，这个模板只是纯HTML的，不包含Python代码。

en: Now we are able to extend our to do list.
zh: 这样，我们就完成了添加待办事项这个功能。

en: By the way, if you prefer to use POST data: this works exactly the same way, just use ``request.POST.get()`` instead.
zh: 如果你想通过POST来获取数据，那么用 ``request.POST.get()`` 来代替 ``request.GET.get()`` 就行了。

en: Editing Existing Items
zh: 修改已有待办事项

en: The last point to do is to enable editing of existing items.
zh: 最后，我们需要做的是修改已有待办事项。

en: By using only the routes we know so far it is possible, but may be quite tricky. But Bottle knows something called "dynamic routes", which makes this task quite easy.
zh: 仅使用我们当前了解到的route类型，是可以完成这个任务的，但太取巧了。Bottle还提供了一种 ``动态route`` ，可以更简单地实现。

en: The basic statement for a dynamic route looks like this::
zh: 基本的动态route声明如下::

en: The key point here is the colon. This tells Bottle to accept for ``:something`` any string up to the next slash. Furthermore, the value of ``something`` will be passed to the function assigned to that route, so the data can be processed within the function.
zh: 关键的区别在于那个冒号。它告诉了Bottle，在下一个 ``/`` 之前， ``:something`` 可以匹配任何字符串。 ``:something`` 匹配到的字符串会传递给回调函数，进一步地处理。

en: For our ToDo list, we will create a route ``@route('/edit/:no)``, where ``no`` is the id of the item to edit.
zh: 在我们的待办事项应用里，我们创建一个route( ``@route('edit/:no')`` ), ``no`` 是待办事项在数据库里面的ID。

en: The code looks like this::
zh: 对应的代码如下。

en: It is basically pretty much the same what we already did above when adding new items, like using ``GET`` data etc. The main addition here is using the dynamic route ``:no``, which here passes the number to the corresponding function. As you can see, ``no`` is used within the function to access the right row of data within the database.
zh: 这和之前的添加待办事项类似，主要的不同点在于使用了动态的route( ``:no`` )，它可将ID传给route对应的回调函数。如你所见，我们在 ``edit_item`` 函数中使用了 ``no`` ，从数据库中获取数据。

en: The template ``edit_task.tpl`` called within the function looks like this::
zh: 对应的 ``edit_task.tpl`` 模板如下。

en: Again, this template is a mix of Python statements and HTML, as already explained above.
zh: 再一次，模板中混合了HTML代码和Python代码，之前已解释过。

en: A last word on dynamic routes: you can even use a regular expression for a dynamic route, as demonstrated later.
zh: 你也可在动态route中使用正则表达式，稍后会提及。

en: Validating Dynamic Routes
zh: 验证动态route

en: Using dynamic routes is fine, but for many cases it makes sense to validate the dynamic part of the route. For example, we expect an integer number in our route for editing above. But if a float, characters or so are received, the Python interpreter throws an exception, which is not what we want.
zh: 在某些场景下，需要验证route中的可变部分。例如，在上面的例子中，我们的 ``no`` 需要是一个整形数，如果我们的输入是一个浮点数，或字符串，Python解释器将会抛出一个异常，这并不是我们想要的结果。

en: For those cases, Bottle offers the ``@validate`` decorator, which validates the "input" prior to passing it to the function. In order to apply the validator, extend the code as follows::
zh: 对应上述情况，Bottle提供了一个 ``@validate`` 修饰器，可在用户输入被传递给回调函数之前，检验用户数据的合法性。代码例子如下。

en: At first, we imported ``validate`` from the Bottle framework, than we apply the @validate-decorator. Right here, we validate if ``no`` is an integer. Basically, the validation works with all types of data like floats, lists etc.
zh: 首先，我们从Bottle中导入了 ``validate`` ，然后在route中使用了。在这里，我们验证 ``no`` 是否是一个整形数。基本上， ``validate`` 可用于其它类型，例如浮点数，列表等等。

en: Save the code and call the page again using a "403 forbidden" value for ``:no``, e.g. a float. You will receive not an exception, but a "403 - Forbidden" error, saying that an integer was expected.
zh: 保存更改，如果用户提供的 ``:no`` 不是一个整形数，而是一个浮点数或其他类型，将返回一个"403 forbidden"页面，而不是抛出异常。

en: Dynamic Routes Using Regular Expressions
zh: 在动态route中使用正则表达式

en: Bottle can also handle dynamic routes, where the "dynamic part" of the route can be a regular expression.
zh: Bottle允许在动态route中使用正则表达式。

en: So, just to demonstrate that, let's assume that all single items in our ToDo list should be accessible by their plain number, by a term like e.g. "item1". For obvious reasons, you do not want to create a route for every item. Furthermore, the simple dynamic routes do not work either, as part of the route, the term "item" is static.
zh: 我们假设需要通过 ``item1`` 这样的形式来访问数据库中id为1的待办事项。显然，我们不想为每个待办事项都创建一个route。鉴于route中的"item"部分是固定的，简单的route就无法满足需求了，我们需要在route中使用正则表达式。

en: As said above, the solution is a regular expression::
zh: 使用正则表达式的解决方法如下。

en: Of course, this example is somehow artificially constructed - it would be easier to use a plain dynamic route only combined with a validation. Nevertheless, we want to see how regular expression routes work: the line ``@route(/item:item_#[0-9]+#)`` starts like a normal route, but the part surrounded by # is interpreted as a regular expression, which is the dynamic part of the route. So in this case, we want to match any digit between 0 and 9. The following function "show_item" just checks whether the given item is present in the database or not. In case it is present, the corresponding text of the task is returned. As you can see, only the regular expression part of the route is passed forward. Furthermore, it is always forwarded as a string, even if it is a plain integer number, like in this case.
zh: 当然，这个例子是我们想象出来的，去掉"item1"中的"item"，直接使用"1"会更简单。虽然如此，我们还是想为你展示在route的正则表达式: ``@route(/item:item_#[1-9]+#)`` 和一个普通的route差不多，但是在两个"#"中的字符就是一个正则表达式，是该route中的动态部分，匹配从0到9的数字。在处理"/item9"这样的请求的时候，正则表达式会匹配到"9"，然后将"9"做为item参数传递给show_item函数，而不是"item9"。注意，这里传给show_item函数的"9"，是一个字符串。

en: Returning Static Files
zh: 返回静态文件

en: Sometimes it may become necessary to associate a route not to a Python function, but just return a static file. So if you have for example a help page for your application, you may want to return this page as plain HTML. This works as follows::
zh: 有时候，我们只是想返回已有的静态文件。例如我们的应用中有个静态的帮助页面help.html，我们不希望每次访问帮助页面的时候都动态生成。

en: At first, we need to import the ``static_file`` function from Bottle. As you can see, the ``return static_file`` statement replaces the ``return`` statement. It takes at least two arguments: the name of the file to be returned and the path to the file. Even if the file is in the same directory as your application, the path needs to be stated. But in this case, you can use ``'.'`` as a path, too. Bottle guesses the MIME-type of the file automatically, but in case you like to state it explicitly, add a third argument to ``static_file``, which would be here ``mimetype='text/html'``. ``static_file`` works with any type of route, including the dynamic ones.
zh: 首先，我们需要从Bottle中导入 ``static_file`` 函数。它接受至少两个参数，一个是需要返回的文件的文件名，一个是该文件的路径。即使该文件和你的应用在同一个目录下，还是要指定文件路径(可以使用".")。Bottle会猜测文件的MIME类型，并自动设置。如果你想显式指定MIME类型，可以在static_file函数里面加上例如 ``mimetype='text/html'`` 这样的参数。 ``static_file`` 函数可和任何route配合使用，包括动态route。

en: Returning JSON Data
zh: 返回JSON数据

en: There may be cases where you do not want your application to generate the output directly, but return data to be processed further on, e.g. by JavaScript. For those cases, Bottle offers the possibility to return JSON objects, which is sort of standard for exchanging data between web applications. Furthermore, JSON can be processed by many programming languages, including Python
zh: 有时我们希望返回JSON，以便在客户端使用JavaScript来生成页面，Bottle直接支持返回JSON数据。JSON似乎已经是Web应用之间交换数据的标准格式了。更进一步，JSON可以被很多语言解析处理，包括Python。

en: So, let's assume we want to return the data generated in the regular expression route example as a JSON object. The code looks like this::
zh: 我们假设现在需要返回JSON数据。

en: As you can, that is fairly simple: just return a regular Python dictionary and Bottle will convert it automatically into a JSON object prior to sending. So if you e.g. call "http://localhost/json1" Bottle should in this case return the JSON object ``{"Task": ["Read A-byte-of-python to get a good introduction into Python"]}``.
zh: 很简单，只需要返回一个Python中的字典就可以了，Bottle会自动将其转换为JSON，再传输到客户端。如果你访问"http://localhost/json1"，你应能得到 ``{"Task": ["Read A-byte-of-python to get a good introduction into Python"]}`` 类型的JSON数据。

en: Catching Errors
zh: 捕获错误

en: The next step may is to catch the error with Bottle itself, to keep away any type of error message from the user of your application. To do that, Bottle has an "error-route", which can be a assigned to a HTML-error.
zh: 为了避免用户看到出错信息，我们需要捕获应用运行时出现的错误，以提供更友好的错误提示。Bottle提供了专门用于捕获错误的route。

en: In our case, we want to catch a 403 error. The code is as follows::
zh: 例如，我们想捕获403错误。

en: So, at first we need to import ``error`` from Bottle and define a route by ``error(403)``, which catches all "403 forbidden" errors. The function "mistake" is assigned to that. Please note that ``error()`` always passes the error-code to the function - even if you do not need it. Thus, the function always needs to accept one argument, otherwise it will not work.
zh: 首先，我们需要从Bottle中导入 ``error`` ，然后通过 ``error(403)`` 来定义创建一个route，用于捕获所有"403 forbidden"错误。注意，该route总是会将error-code传给 ``mistake()`` 函数，即使你不需要它。所以回调函数至少要接受一个参数，否则会失效。

en: Again, you can assign more than one error-route to a function, or catch various errors with one function each. So this code::
zh: 一样的，同一个回调函数可以捕获多种错误。

en: works fine, the following one as well::
zh: 效果和下面一样。

en: Summary
zh: 总结

en: After going through all the sections above, you should have a brief understanding how the Bottle WSGI framework works. Furthermore you have all the knowledge necessary to use Bottle for your applications.
zh: 通过以上章节，你应该对Bottle框架有了一个大致的了解，可以使用Bottle进行开发了。

en: The following chapter give a short introduction how to adapt Bottle for larger projects. Furthermore, we will show how to operate Bottle with web servers which perform better on a higher load / more web traffic than the one we used so far.
zh: 接下来的章节会简单介绍一下，如何在大型项目中使用Bottle。此外，我们还会介绍如何将Bottle部署到更高性能的Web服务器上。

en: Server Setup
zh: 安装服务器

en: So far, we used the standard server used by Bottle, which is the `WSGI reference Server`_ shipped along with Python. Although this server is perfectly suitable for development purposes, it is not really suitable for larger applications. But before we have a look at the alternatives, let's have a look how to tweak the settings of the standard server first.
zh: 到目前为止，我们还是使用Bottle内置的，随Python一起发布的 `WSGI reference Server`_ 服务器。尽管该服务器十分适合用于开发环境，但是它确实不适用于大项目。在我们介绍其他服务器之前，我们先看看如何优化内置服务器的设置。

en: Running Bottle on a different port and IP
zh: 更改服务器的端口和IP

en: As standard, Bottle serves the pages on the IP adress 127.0.0.1, also known as ``localhost``, and on port ``8080``. To modify the setting is pretty simple, as additional parameters can be passed to Bottle's ``run()`` function to change the port and the address.
zh: 默认的，Bottle会监听127.0.0.1(即 ``localhost`` )的 ``8080`` 端口。

en: To change the port, just add ``port=portnumber`` to the run command. So, for example::
zh: 如果要更改该设置，更改 ``run`` 函数的参数即可。

en: would make Bottle listen to port 80.
zh: 更改端口，监听80端口

en: To change the IP address where Bottle is listening::
zh: 更改监听的IP地址

en: Of course, both parameters can be combined, like::
zh: 可同时使用

en: The ``port`` and ``host`` parameter can also be applied when Bottle is running with a different server, as shown in the following section.
zh: 当Bottle运行在其他服务器上面时， ``port`` 和 ``host`` 参数依然适用，稍后会介绍。

en: Running Bottle with a different server
zh: 在其他服务器上运行

en: As said above, the standard server is perfectly suitable for development, personal use or a small group of people only using your application based on Bottle. For larger tasks, the standard server may become a bottleneck, as it is single-threaded, thus it can only serve one request at a time.
zh: 在大型项目上，Bottle自带的服务器会成为一个性能瓶颈，因为它是单线程的，一次只能响应一个请求。

en: But Bottle has already various adapters to multi-threaded servers on board, which perform better on higher load. Bottle supports Cherrypy_, Fapws3_, Flup_ and Paste_.
zh: Bottle已经可以工作在很多多线程的服务器上面了，例如 Cherrypy_, Fapws3_, Flup_ 和 Paste_ ，所以我们建议在大型项目上使用高性能的服务器。

en: If you want to run for example Bottle with the Paste server, use the following code::
zh: 如果想运行在Paste服务器上面，代码如下(译者注：需要先安装Paste)。

en: This works exactly the same way with ``FlupServer``, ``CherryPyServer`` and ``FapwsServer``.
zh: 其他服务器如 ``FlupServer``, ``CherryPyServer`` 和 ``FapwsServer`` 也类似。

en: Running Bottle on Apache with mod_wsgi
zh: 使用 mod_wsgi_ 运行在Apache上

en: Maybe you already have an Apache_ or you want to run a Bottle-based application large scale - then it is time to think about Apache with mod_wsgi_.
zh: 或许你已经有了一个 Apache_ 服务器，那么可以考虑使用 mod_wsgi_ 。

en: We assume that your Apache server is up and running and mod_wsgi is working fine as well. On a lot of Linux distributions, mod_wsgi can be easily installed via whatever package management system is in use.
zh: 我们假设你的Apache已经能跑起来，且mod_wsgi也能工作了。在很多Linux发行版上，都能通过包管理软件简单地安装mod_wsgi。

en: Bottle brings an adapter for mod_wsgi with it, so serving your application is an easy task.
zh: Bottle已经自带用于mod_wsgi的适配器，所以让Bottle跑在mod_wsgi上面是很简单的。

en: In the following example, we assume that you want to make your application "ToDo list" accessible through ``http://www.mypage.com/todo`` and your code, templates and SQLite database are stored in the path ``/var/www/todo``.
zh: 接下来的例子里，我们假设你希望通过 ``http://www.mypage.com/todo`` 来访问"ToDo list"这个应用，且代码、模板、和SQLite数据库存放在 ``/var/www/todo`` 目录。

en: When you run your application via mod_wsgi, it is imperative to remove the ``run()`` statement from your code, otherwise it won't work here.
zh: 如果通过mod_wsgi来运行你应用，那么必须从代码中移除 ``run()`` 函数。

en: After that, create a file called ``adapter.wsgi`` with the following content::
zh: 然后，创建一个 ``adapter.wsgi`` 文件，内容如下。

en: and save it in the same path, ``/var/www/todo``. Actually the name of the file can be anything, as long as the extension is ``.wsgi``. The name is only used to reference the file from your virtual host.
zh: 将其保存到 ``/var/www/todo`` 目录下面。其实，可以给该文件起任何名字，只要后缀名为 ``.wsgi`` 即可。

en: Finally, we need to add a virtual host to the Apache configuration, which looks like this::
zh: 最后，我们需要在Apache的配置中添加一个虚拟主机。

en: After restarting the server, your ToDo list should be accessible at ``http://www.mypage.com/todo``
zh: 重启Apache服务器后，即可通过 ``http://www.mypage.com/todo`` 来访问你的应用。

en: Final Words
zh: 结语

en: Now we are at the end of this introduction and tutorial to Bottle. We learned about the basic concepts of Bottle and wrote a first application using the Bottle framework. In addition to that, we saw how to adapt Bottle for large tasks and serve Bottle through an Apache web server with mod_wsgi.
zh: 现在，我们这个教程已经结束了。我们学习了Bottle的基础知识，然后使用Bottle来写了第一个应用。另外，我们还介绍了如何在大型项目中使用Bottle，以及使用mod_wsgi在Apache中运行Bottle应用。

en: As said in the introduction, this tutorial is not showing all shades and possibilities of Bottle. What we skipped here is e.g. receiving file objects and streams and how to handle authentication data. Furthermore, we did not show how templates can be called from within another template. For an introduction into those points, please refer to the full `Bottle documentation`_ .
zh: 我们并没有在这份教程里介绍Bottle的方方面面。我们没有介绍如何上传文件，验证数据的可靠性。还有，我们也没介绍如何在模板中调用另一个模板。以上，可以在 `Bottle documentation`_ 中找到答案。

en: Complete Example Listing
zh: 完整代码

en: As the ToDo list example was developed piece by piece, here is the complete listing:
zh: 我们是一步一步地开发待办事项列表的，这里是完整的代码。

en: Main code for the application ``todo.py``::
zh: ``todo.py``

en: Template ``make_table.tpl``::
zh: ``make_table.tpl``模板

en: Template ``edit_task.tpl``::
zh:  ``edit_task.tpl`` 模板

en: Template ``new_task.tpl``::
zh: ``new_task.tpl`` 模板
