Bottle

Bottle - это быстрый, простой и легкий WSGI микро веб-фреймворк для Python. Он распространяется в виде одного файла-модуля и не имеет никаких зависимостей, кроме стандартной библиотеки Python.

Оригинальный документ

Учебник: Приложение Список-Задач

Этот учебник краткое введение в WSGI фреймфорк Bottle. Основной целью является то, что после прочтения этого учебника вы сможете создавать проекты использующие Bottle. В этом документе, будут рассмотрены не все возможности , но по крайней мере, основные и важные, такие, как маршрутизация, использование Bottle шаблонов для форматирования вывода и обработка GET / POST параметров.

Для понимания изложенного здесь материала не обязательно иметь базовые знания о WSGI, т.к. Bottle пытается скрыть механизмы работы WSGI от пользователя. Вы должны иметь чёткое понимание языка программирования Python . Кроме того, примеры используемые в учебнике извлекают и хранят данные в базе данных SQL, так что основные сведения о SQL помогут, но не являются ключевыми для понимания концепции Bottle. В некоторых примерах Bottle отправляет выходные данные браузеру отформатированными при помощи HTML. Таким образом, общее представление об HTML тегах будет полезным.

Во введении в Bottle, промежуточный код на Python максимально короткий для сохранения понимания. Кроме того, весь учебный код работает нормально, но не стоит его использовать его в "сыром" виде на общедоступном веб-сервере. Для того, его использовать лучше добавить например, больше обработок ошибок, защитить базу данных с помощью пароля, проверок и ошибок ввода т.д.

Цели

В конце этого учебника мы получим простое веб-ориентированный Список-Задач. Список представляет собой для каждой записи текст( максимум 100 символов) и статус (0 для выполненного, 1 для открытой). Через веб-ориентированный интерфейс пользователь может открыть запись для просмотра, редактирования или добавить новую запись.

В время разработки, все страницы будут доступны только локально, но в дальнейшем будет показано как адаптировать приложение для "реального" сервера, включая использование с Apache’s mod_wsgi.

Bottle будет делать маршрутизацию и форматирование вывода, при помощи шаблонов. Элементы списка будут храниться в базе данных SQLite. Чтение и запись в / из базы данных будет осуществляться с помощью кода на Python.

В итоге мы будем иметь приложение со следующими страницами и функциональными возможностями:

• Стартовая страница http://localhost:8080/todo

• Добавление новых элементов в список: http://localhost:8080/new

• Страница для редактирования элементов: http://localhost:8080/edit/:no

• Проверки данных, заданных динамическими маршрутами с @validate в качестве декоратора

• Определять ошибки

Прежде чем мы начнём ...

Установка Bottle

Предположим, что у вас установлен Python (версия 2.5 или выше) и все, что вам нужно это установить Bottle в дополнение к этому. Bottle не имеет других зависимостей, кроме самого Python.

Вы можете установить Bottle вручную или использовать easy_install от Python: easy_install bottle

Программное обеспечение которое понадобится в дальнейшем

Так как мы используем SQLite3 в качестве базы данных, убедитесь, что он установлен. В системах Linux, большинство дистрибутивов имеют SQLite3 установленным по умолчанию. SQLite также доступен для Windows and MacOS X .

Кроме того, необходим Pysqlite, модуль Python для доступа к базам данных SQLite. Опять же, во многих дистрибутивах Linux предустановлен этот модуль (их часто называют "Python-sqlite3"), в противном случае можно просто установить вручную или через easy_install pysqlite .

Примечание: Многие старые системы имеют предустановленным sqlite2 . Все примеры тоже будут корректно работать с этой версией. Вам просто нужно импортировать соответствующий модуль Python под названием "SQLite" вместо "sqlite3", как оно используется в примерах ниже.

Создание базы данных SQL

Во-первых, мы должны создать базу данных, мы используем её в дальнейшем. Для этого запустите SQLite с помощью команды sqlite3 todo.db . Это позволит создать пустую базу данных под названием "todo.sql" и вы увидите приглашение SQLite , которое может выглядеть следующим образом: sqlite> . Прямо здесь, ввести следующие команды:

CREATE TABLE todo (id int PRIMARY KEY, task char(100) NOT NULL, status bool NOT NULL);
INSERT INTO todo (task,status) VALUES ('Read A-byte-of-python to get a good introduction into Python',0);
INSERT INTO todo (task,status) VALUES ('Visit the Python website',1);
INSERT INTO todo (task,status) VALUES ('Test various editors for and check the syntax highlighting',1);
INSERT INTO todo (task,status) VALUES ('Choose your favorite WSGI-Framework',0);

Первая строка создаёт таблицу которая называется "todo" с тремя столбцами "id", "task" и "status". "id" является уникальным идентификатором для каждой строки, который используется в дальнейшем для обращения к строкам. В графе "task" хранится текст, который описывает задачу, он может быть не более 100 символов. Наконец, колонка "status" используется для обозначения задачи как открыта (значение 1) или закрыта (значение 0).

Использование Bottle для веб-приложения Список-Задач (ToDo)

Теперь настало время применить Bottle с целью создания веб-приложения. Но во-первых, мы должны рассмотреть основную концепцию Bottle: маршруты.

Понимание маршрутов

В принципе, каждая страница отображается в браузере после динамической генерации когда вызывается адрес страницы. Таким образом, нет статического контента. Это именно то, что называется "route" в Bottle: некий адрес на сервере. Так, например, когда страница http://localhost:8080/todo вызывается из браузера, Bottle "перехватывает" вызов и проверяет, есть ли (Python) функция, определённая для маршрута(route) "todo". Если да, то Bottle будет выполнять соответствующий код Python и возвращает результат.

Первый шаг - Отображение всех открытых задач

Таким образом, после понимания концепции маршрутов, давайте создадим один первый. Цель состоит в том, чтобы увидеть все открытые элементы из Списка-Заданий(ToDo):

import sqlite3
from bottle import route, run

@route('/todo')
def todo_list():
    conn = sqlite3.connect('todo.db')
    c = conn.cursor()
    c.execute("SELECT id, task FROM todo WHERE status LIKE '1'")
    result = c.fetchall()
    return str(result)

run()

Сохраните код "todo.py", предпочтительно в той же папке где и файл "todo.db". В противном случае, вам необходимо добавить путь к "todo.db" в параметрах sqlite3.connect().

Давайте посмотрим, что мы только что сделали: мы импортировали необходимый модуль "sqlite3" для доступа к базе данных SQLite и из Bottle мы импортировали "route" и "run". Оператор run() просто запускает веб-сервер находящийся в Bottle. По умолчанию веб-сервер обслуживает страницы на localhost и порту 8080. Кроме того, мы импортировали "route", функцию которая отвечает в Bottle за маршрутизацию. Как вы можете видеть, мы определили одну функцию " todo_list ()", с несколькими строками кода чтения из базы данных. Важным моментом является определение декоратора @route('/todo') непосредственно перед определением def todo_list(). Делая это, мы связываем эту функцию с маршрутом "/todo", так что каждый раз, когда браузер вызывает http://localhost:8080/todo , Bottle возвращает результат функции "todo_list ()". Так работает маршрутизации внутри Bottle.

На самом деле вы можете к функции связать более чем один маршрут.

Так следующий код

...
@route('/todo')
@route('/my_todo_list')
def todo_list():
...

тоже будет работать нормально. Что не будет работать, связывание одного маршрута с более чем одной функцией.

То, что вы увидите в браузере является данными которые описаны в параметрах return. В этом примере, мы должны преобразовать "result" в строку через функцию str() , т.к. Bottle ожидает строку или список строк в параметрах оператора return. Но здесь, в результате запроса к базе данных возвращается список кортежей, который является стандартом определеным в Python DB API .

Теперь, после понимания немного скрипта выше , настало время исполнить его и посмотреть результат самостоятельно. Помните о том, что в Linux-/ Unix подобных системах сначала файл "todo.py" необходимо сделать исполняемым . После этого достаточно просто запустить python todo.py и открыть страницу http://localhost:8080/todo в вашем браузере. Если Вы не ошиблись в написании скрипта, результат должен выглядеть следующим образом:

[(2, u'Visit the Python website'), (3, u'Test various editors for and check the syntax highlighting')]

Если это так - поздравляем!. Вы теперь успешной пользователь Bottle. В случае, если что-то не работает, и вам необходимо внести некоторые изменения в скрипт, не забудьте остановить Bottle который обслуживает страницу, в противном случае исправленный вариант не будет загружен.

На самом деле, будет не очень интересно и не приятно читать вывод . Это сырой результат возвращённый SQL-запросом.

Таким образом, в следующем шаге мы красивым способом отформатируем вывод . Но прежде, чем мы это делаем, мы делаем нашу жизнь легче.

Отладка и Авто-Обновление

Возможно, вы уже испытали Bottle, что отправляет краткое сообщение об ошибке в браузере, если что-то в сценарии не так, например, соединение с базой данных не работает. Для отладки полезно получить более подробную информацию. Это можно легко сделать, добавив следующее заявление в скрипт:

from bottle import run, route, debug
...
#add this at the very end:
debug(True)
run()

При включении "debug", вы получите полную трассировку стека интерпретатора Python, который обычно содержит полезную информацию для поиска ошибок. Кроме того, шаблоны (см. ниже) не будут кэшироваться, таким образом, изменения в шаблон вступит в силу без остановки сервера.

Обратите внимание, что опция debug(True) должна быть использована только при разработке, не должны использоваться в рабочей обстановке.

Дальнейшей полезным свойством является автоматическая перезагрузка, которая может быть включена по изменению параметров run()

run(reloader=True)

Это позволит автоматически выявлять изменения в скрипте и загружать новый вариант, когда он будет вызван повторно, без необходимости остановки и запуска сервера.

Опять же, в основном предполагается использовать функцию при разработке, а не на рабочих системах.

Шаблоны для форматирования вывода

Теперь давайте посмотрим как оформить вывод скрипта в надлежащем формате.

На самом деле Bottle рассчитывает получить строку или список строк из функции и возвращает их при помощи встроенного сервера в браузер. Bottle не беспокоиться о содержании самой строки, поэтому она может быть отформатирована как текст с разметкой HTML.

Шаблоны хранятся как отдельные файлы, имеющие расширение ".tpl". Шаблоны могут содержать любой тип текста (который будет, скорее всего, HTML-разметка, смешанная с операторами Python). Кроме того, шаблоны могут принимать аргументы, например, в результат запроса к базе данных, который затем будет красиво отформатированный в шаблоне.

Прямо здесь, мы будем передавать результат нашего запроса показа открытых позиций в Списке Задач, в виде в простой таблицы с двумя столбцами: первый столбец будет содержать идентификатор элемента, второй столбец текста. Результирующие множество, как мы видели выше, список кортежей, каждый кортеж содержит один набор результатов.

Чтобы включить шаблон в нашем примере, просто добавьте следующие строки:

from bottle import from bottle import route, run, debug, template
...
result = c.fetchall()
c.close()
output = template('make_table', rows=result)
return output
...

Таким образом, мы делаем две вещи: во-первых, мы импортируем "template" из Bottle, с тем чтобы иметь возможность использовать шаблоны. Во-вторых, назначить вывод как шаблон "make_table" переменной "output", которая затем возвращается. В дополнение к вызову шаблона, назначить "result", который мы получили в результате запроса к базе данных, для переменной "rows", которая в дальнейшем используется в шаблоне. При необходимости можно назначить более чем одну переменную / значение в шаблон.

Шаблоны всегда возвращает список строк, поэтому нет необходимости дополнительного конвертирования. Конечно, мы можем сохранить одну строку кода записав return template('make_table', rows=result) , что дает тот же результат, как указано выше.

Теперь настало время, чтобы написать соответствующий шаблон, который выглядит следующим образом:

%#template to generate a HTML table from a list of tuples (or list of lists, or tuple of tuples or ...)
<p>The open items are as follows:</p>
<table border="1">
%for row in rows:
  <tr>
  %for r in row:
    <td>{{r}}</td>
  %end
  </tr>
%end
</table>

Сохраните код как "make_table.tpl" в тот же каталог, где сохранен"todo.py" .

Давайте посмотрим на код: Каждая строка, начинающиеся с% интерпретируется как код Python. Обратите внимание, что, конечно, разрешается только правильные определения Python , в противном случае шаблон будет вызывать исключения, как и любой другой код Python. Другие строки просто HTML-разметка.

Как вы видите, мы используем Python оператор "for" два раза, чтобы перебрать "rows". Как мы видели выше, "rows" является переменной, которая содержит результат запроса к базе данных, т.е. список кортежей.Первый "for"-оператор получает доступ к кортежам в списке, второй элементы в кортеже, которые находятся в каждой ячейке таблицы. Важным является тот факт, что вам нужно дополнительно закрыть все "for" и "if", "while" и т.д. оператором %end , в противном случае результат может быть не тем, что вы ожидаете.

Если вам нужно получить доступ к переменной внутри шаблона и вне кода Python , нужно поместить его в двойные фигурные скобки. Это говорит шаблону вставить фактическое значение переменной правильное место.

Выполните скрипт еще раз и посмотрите на вывод. По-прежнему не очень хорошо, но по крайней мере лучше читается, чем список кортежей. Конечно, вы можете приукрасить с помощью очень простой HTML-разметки , например, с использованием in-line стилей, чтобы улучшить вывод.

Использование GET и POST значений

Мы можем просмотреть все открытые Задачи надлежащим образом, теперь мы переходим к следующему шагу, который является добавлением новых пунктов в Список Задач. Новый пункт, должен быть получен из обычной HTML-формы, которая отправляет свои данные через GET-метод.

Чтобы сделать это, мы сначала добавляем новый маршрут на наш скрипт и объясняем маршруту, что он должен получить GET-данные:

from bottle import route, run, debug, template, request
...
return template('make_table', rows=result)
...

@route('/new', method='GET')
def new_item():

    new = request.GET.get('task', '').strip()

    conn = sqlite3.connect('todo.db')
    c = conn.cursor()

    query = "INSERT INTO todo (task,status) VALUES ('%s',1)" %new
    c.execute(query)
    conn.commit()

    c.execute("SELECT last_insert_rowid()")
    new_id = c.fetchone()[0]
    c.close

    return '<p>The new task was inserted into the database, the ID is %s</p>

Чтобы получить доступ к GET (или POST) данным, мы должны импортировать "request" из Bottle. Чтобы назначить фактические данные переменной, мы используем определение  request.GET.get('task','').strip() , где "task" это имя GET-данного, к которому мы хотим получить доступ. Вот и все. Если ваши GET-данные содержат более одной переменной, несколько request.GET.get() могут быть использованы и назначены для других переменных.

В остальной части кода просто обработка полученных данных: запись в базу данных, получение соответствующего идентификатора из базы данных и формирование вывода.

Но где мы можем получить GET-данные? Ну, мы можем использовать статические HTML страницы содержащие форму. Или, что мы делаем сейчас, заключается в использовании шаблона для вывода, когда вызывается маршрут "/new" без GET-данных.

Код, необходимо будет расширить до:

...
@route('/new', method='GET')
def new_item():

if request.GET.get('save','').strip():

    new = request.GET.get('task', '').strip()
    conn = sqlite3.connect('todo.db')
    c = conn.cursor()

    query = "INSERT INTO todo (task,status) VALUES ('%s',1)" %new
    c.execute(query)
    conn.commit()

    c.execute("SELECT last_insert_rowid()")
    new_id = c.fetchone()[0]
    c.close

    return '<p>The new task was inserted into the database, the ID is %s</p>' %new_id

else:
    return template('new_task.tpl')
...

"new_task.tpl" выглядит следующим образом:

<p>Add a new task to the ToDo list:</p>
<form action="/new" method="GET">
<input type="text" size="100" maxlength="100" name="task">
<input type="submit" name="save" value="save">
</form>

Вот и все. Как вы видите, в этот раз шаблон простой HTML .

Теперь мы можем расширить наш Cписок-Задач.

Кстати, если вы предпочитаете использовать POST-данные: Это работает точно так же, просто используйте вместо request.POST.get().

Изменение существующих элементов

Последний пункт Списка Задач, это позволить редактирование существующих элементов.

С помощью маршрутов, которые мы знаем до сих пор это возможно, но может быть чуть сложнее. Но Bottle знает так называемых "динамические маршруты", что делает эту задачу простой.

Основные определения для динамического маршрута выглядит следующим образом:

@route('/myroute/:something')

Ключевой момент здесь состоит в использовании двоеточия. Это говорит Bottle принять за ":something" любую строку, до следующего слэша. Кроме того, значение "something" будут переданы функции, установленного для этого маршрута, поэтому данные могут быть обработаны внутри функции.

В нашем Списке Задач, мы создадим маршрут @route('/edit/:no) , где "no" идентификатор элемента для редактирования.

Код выглядит так:

@route('/edit/:no', method='GET')
def edit_item(no):

    if request.GET.get('save','').strip():
        edit = request.GET.get('task','').strip()
        status = request.GET.get('status','').strip()

        if status == 'open':
            status = 1
        else:
            status = 0

        conn = sqlite3.connect('todo.db')
        c = conn.cursor()
        query = "UPDATE todo SET task = '%s', status = '%s' WHERE id LIKE '%s'" % (edit,status,no)
        c.execute(query)
        conn.commit()

        return '<p>The item number %d was successfully updated</p>' %no

    else:
        conn = sqlite3.connect('todo.db')
        c = conn.cursor()
        query = "SELECT task, status FROM todo WHERE id LIKE '%d'" %no
        c.execute(query)
        cur_data = c.fetchone()

        return template('edit_task', old = cur_data, no = no)

Это в основном почти то же, что мы уже делали раньше при добавлении новых элементов, как использование "GET"-данных и т.д. Основным изменением здесь является использование динамического маршрута":no", который проходит здесь как номер соответствующей функции. Как видите, "no" используется в функции чтобы получить доступ к нужной строке данных в базе данных.

Шаблон "edit_task.tpl" вызываемый в функции выглядит следующим образом:

%#template for editing a task
%#the template expects to receive a value for "no" as well a "old", the text of the selected ToDo item
<p>Edit the task with ID = {{no}}</p>
<form action="/edit/{{no}}" method="get">
<input type="text" name="task" value="{{old[0]}}" size="100" maxlength="100">
<select name="status">
<option>open</option>
<option>closed</option>
</select>
<br/>
<input type="submit" name="save" value="save">
</form>

Опять же, этот шаблон представляет собой смесь определений Python и HTML, как уже говорилось выше.

Последнее слово об динамических маршрутах: вы даже можете использовать регулярные выражения для динамического маршрута. Но эта тема обсуждается не здесь.

Проверка динамических маршрутов

Использование динамических маршрутов это хорошо, но во многих случаях имеет смысл проверить динамическую часть маршрута. Например, мы ожидаем целое число в нашем выше описанном маршруте для редактирования. Но если число с плавающей точкой, символы итд поступают, интерпретатор Python генерирует исключение, которое не то, что мы хотим.

Для таких случаев, Bottle предлагает @valdiate декоратор, который проверяют "input" (ввод) до передачи его в функцию. Для того, чтобы применить проверки, дополним код следующим образом:

from bottle import route, run, debug, template, request, validate

...

@route('/edit/:no', method='GET')
@validate(no=int)
def edit_item(no):

...

Сначала мы импортировали "validate" из модуля Bottle , затем мы применяем @validate-декоратор. Прямо здесь, мы проверяем, если "no" является целым числом. В принципе, проверка работает со всеми типами данных, т.е. числа с плавающей точкой, списки т.д.

Сохраните код и вызовите еще раз страницу, будет использовано значение "403 Forbidden" для ":no", например, с плавающей точкой. Вы получите не исключение, а "403 Forbidden" ошибка, которая говорит, что ожидалось целое число.

Отслеживание ошибок

Следующим шагом может быть, отлавливание ошибок с помощью Bottle, препятствуя любому типу сообщение об ошибке от пользователей вашего приложения. Чтобы сделать это, Bottle имеет "error-route", который может быть сопоставлен на HTML-ошибкой.

В нашем случае, мы хотим поймать 403 ошибку. Код выглядит следующим образом:

from bottle import route, run, debug, template, request, validate, error

...

@error(403)
def mistake(code):
    return 'The parameter you passed has the wrong format!'

Таким образом, сначала необходимо импортировать "error" из Bottle, и определить маршрут как error(403) , который ловит все "403 Forbidden" ошибки. Функция "mistake" сопоставлена для на этого. Обратите внимание, что error() всегда передается ошибка-код в функцию - даже если вы в ней не нуждаетесь. Таким образом, функция всегда должна принимать один аргумент, в противном случае она не будет работать.

Опять же, вы можете назначить более одного error-route для функции, или поймать различные ошибки, выполняя одну и ту же функцию для каждой. Так вот этот код::

@error(404)
@error(403)
def mistake(code):
    return 'There is something wrong!'

работает отлично, следующие один, а также:

@error(403)
def mistake403(code):
    return 'The parameter you passed has the wrong format!'

@error(404)
def mistake404(code):
    return 'Sorry, this page does not exist!'

Резюме

После прохождения всех разделов, вы должны иметь краткое понимания того, как Bottle работает Bottle WSGI фреймворк. Кроме того у вас есть все знания, необходимые для использования Bottle для ваших приложений.

В следующей главе дается краткое введение о том, как адаптировать Bottle для более крупных проектов. Кроме того, мы покажем, как работает Bottle с веб-серверами, которые лучше подходят под высокую нагрузку / большой трафик, чем тот, который мы использовали до сих пор.

Настройка сервера

До сих пор мы использовали стандартный сервер который используется в Bottle, который является стандартно поставляемым WSGI reference Server вместе с Python. Хотя этот сервер идеально подходит для целей разработки, это не очень подходит для больших приложений. Но прежде чем мы рассмотрим варианты, сначала давайте посмотрим, как подогнать настройки стандартного сервера.

Запуск Bottle на другом порту и IP

В качестве стандартного, Bottle обслуживает страницы на IP-адрес 127.0.0.1, также известный как "localhost", и на порту "8080". Чтобы изменить настройки нам достаточно просто, передать дополнительные параметры в Bootle'скую run() функцию, чтобы изменить порт и адрес.

Чтобы изменить порт, просто добавьте port=portnumber для запуска команды. Так, например

run(port=80)

Bottle будет слушать порт 80.

Чтобы изменить IP-адрес, где Bottle прослушивает/обслуживает могут быть изменены так:

run(host='123.45.67.89')

Конечно, оба параметра могут быть объединены, например:

run(port=80, host='123.45.67.89')

Параметры port и host могут быть применены, когда Bottle работает с другим сервером, как показано в следующем разделе.

Запуск Bottle с другим сервером

Как говорилось выше, стандартный сервер идеально подходит для локальной разработки, персонального использования или небольшой группы людей. Для более крупных задач, стандартный сервер может стать узким местом, так как он одно-поточный, поэтому он может обслуживать лишь по одному запросу за раз.

Но Bottle уже имеет различные адаптеры для много-поточных серверов, которые лучше подходят при высокой нагрузке. Bottle поддерживает cherryPy , fapws3 , flup and Paste .

Если вы хотите запустить, например Bottle с Paste Server, используйте следующий код:

from bottle import PasteServer от бутылки импорта PasteServer

... ...

run ( server = PasterServer ) запустить (сервер = PasterServer)

Это работает точно так же с FlupServer , CherryPyServer и FapwsServer .

Запуск Bottle на Apache с mod_wsgi

Может быть, у вас уже есть веб-сервер Apache web server , или вы хотите запустить приложение на Bottle в больших масштабах - время подумать о Apache с mod_wsgi .

Мы предполагаем, что ваш сервер Apache установлен и работает, и mod_wsgi работает нормально. На многих дистрибутивов Linux, mod_wsgi могут быть легко установлен через менеджер пакетов.

Bottle предоставляет адаптер для mod_wsgi , так что это является простой задачей.

В следующем примере мы предполагаем, что вы хотите, чтобы ваше приложение "Список Задач" было доступны через "http://www.mypage.com/todo" и ваш код, шаблоны и SQLite база данных хранится по пути "var/www/todo".

Сначала мы должны импортировать "defautl_app" из Bottle в наш небольшой скрипт:

from bottle import route, run, debug, template, request, validate, error, default_app

При запуске приложения через mod_wsgi, крайне важно, чтобы был удалён run() оператор в вашем коде, в противном случае оно не будет работать.

После этого, создайте файл с названием "adapter.wsgi" со следующим содержанием:

import sys
sys.path = ['/var/www/todo/'] + sys.path

import todo
import os

os.chdir(os.path.dirname(__file__))

application = default_app()

Наконец, мы должны добавить виртуальный хост в конфигурацию Apache, который выглядит следующим образом:

    <VirtualHost *>
        ServerName mypage.com
        WSGIDaemonProcess todo user=www-data group=www-data processes=1 threads=5
        WSGIScriptAlias / /var/www/todo/adapter.wsgi

        <Directory /var/www/todo>
            WSGIProcessGroup todo
            WSGIApplicationGroup %{GLOBAL}
            Order deny,allow
            Allow from all
        </Directory>
    </VirtualHost>

Заключительные слова

Сейчас мы находимся в конце этого введения и учебника по Bottle. Мы узнали об основных понятиях Bottle и написали первые приложения, использующие Bottle фреймворк . В дополнение к этому, мы увидели, как адаптировать Bottle для больших задач и сервер Bottle через веб сервер Apache с помощью mod_wsgi.

Как сказано в предисловии, этот учебник не показывает все оттенки и возможности Bottle. То, что мы пропустили здесь например использование регулярных выражений на динамических маршрутах, возврат JSON data, как обслуживать статические файлы и получать Файл объекты и потоки. Кроме того, мы не показали, как шаблоны можно вызвать из другого шаблона. За введениями по этим позициям, обратитесь к полной документации Bottle .

Конечный листинг примера

Главный код приложения todo.py :

import sqlite3
from bottle import route, run, debug, template, request, validate, error

# only needed when you run Bottle on mod_wsgi
from bottle import default_app

@route('/todo')
def todo_list():

    conn = sqlite3.connect('todo.db')
    c = conn.cursor()
    c.execute("SELECT id, task FROM todo WHERE status LIKE '1';")
    result = c.fetchall()
    c.close()

    output = template('make_table', rows=result)
    return output

@route('/new', method='GET')
def new_item():

    if request.GET.get('save','').strip():

        new = request.GET.get('task', '').strip()
        conn = sqlite3.connect('todo.db')
        c = conn.cursor()

        query = "INSERT INTO todo (task,status) VALUES ('%s',1)" %new
        c.execute(query)
        conn.commit()

        c.execute("SELECT last_insert_rowid()")
        new_id = c.fetchone()[0]
        c.close

        return '<p>The new task was inserted into the database, the ID is %s</p>' %new_id

    else:
        return template('new_task.tpl')

@route('/edit/:no', method='GET')
@validate(no=int)
def edit_item(no):

    if request.GET.get('save','').strip():
        edit = request.GET.get('task','').strip()
        status = request.GET.get('status','').strip()

        if status == 'open':
            status = 1
        else:
            status = 0

        conn = sqlite3.connect('todo.db')
        c = conn.cursor()
        query = "UPDATE todo SET task = '%s', status = '%s' WHERE id LIKE '%s'" % (edit,status,no)
        c.execute(query)
        conn.commit()

        return '<p>The item number %s was successfully updated</p>' %no

    else:
        conn = sqlite3.connect('todo.db')
        c = conn.cursor()
        query = "SELECT task FROM todo WHERE id LIKE '%s'" %no
        c.execute(query)
        cur_data = c.fetchone()
        print cur_data

        return template('edit_task', old = cur_data, no = no)

@error(403)
def mistake403(code):
    return 'There is a mistake in your url!'

@error(404)
def mistake404(code):
    return 'Sorry, this page does not exist!'

debug(True)      
run(reloader=True)
#remember to remove reloader=True and debug(True) when you move your application from development to a productive environment.

Шаблон edit_task.tpl :

%#template for editing a task
%#the template expects to receive a value for "no" as well a "old", the text of the selected ToDo item
<p>Edit the task with ID = {{no}}</p>
<form action="/edit/{{no}}" method="get">
<input type="text" name="task" value="{{old[0]}}" size="100" maxlength="100">
<select name="status">
<option>open</option>
<option>closed</option>
</select>
<br/>
<input type="submit" name="save" value="save">
</form>

Шаблон new_task.tpl :

%#template for the form for a new task
<p>Add a new task to the ToDo list:</p>
<form action="/new" method="GET">
<input type="text" size="100" maxlenght="100" name="task">
<input type="submit" name="save" value="save">
</form>

Документация/ВведениеВBottle (последним исправлял пользователь Николай Федоров 2011-03-30 13:27:41)