Chapter 3: Introduction

Обзор

Начало работы

Linux
Mac
Windows

web2py поставляется в виде бинарных пакетов для Windows и Mac OS X. В этот бинарный пакет включен интерпретатор языка Python, поэтому вам нет необходимости предустанавливать его. Существует также версия из исходного кода, которая работает в Windows, Mac, Linux и других Unix подобных системах. Пакет из исходного кода предполагает, что интерпретатор языка Python уже установлен на компьютер.

Web2py не требует инсталляции. Для запуска распакуйте скачанный zip файл для вашей операционной системы и запустите файл web2py.

В Windows запустите:

1
 web2py.exe 

В OS X, запустите:

1
 open web2py.app 

В Unix и Linux, для запуска из пакета исходных кодов наберите:

1
 python2.5 web2py.py 
или
1
./web2py.py
(если вы находитесь в папке web2py)

Для запуска web2py из исходников в Windows, вначале установите Mark Hammond's "Python for Windows extensions, далее запустите:

1
 python2.5 web2py.py 

Программа web2py позволяет использовать различные опции командной строки, о которых мы поговорим позднее.

По умолчанию во время запуска web2py отображает окно запуска, затем виджет графического интерфейса, который попросит вас выбрать: пароль администратора, ip адрес сетевого интерфейса для использования веб сервером, и номер порта для обслуживания запросов. По умолчанию, web2py запускает свой веб сервер по адресу 127.0.0.1:8000, но если вы хотите вы можете запустить web2py на любом другом из доступных ip адресов и портов. Для уточнения доступных ip адресов вы можете воспользоваться командами ipconfig в Windows или ifconfig в OS X и Linux. С настоящего момента мы предполагаем, что web2py запущено по адресу 127.0.0.1:8000. Для того что бы запустить web2py публично на всех ваших сетевых интерфейса используйте 0.0.0.0:80.

image

Если вы не укажете пароль администратора, то административный интерфейс будет выключен, это мера безопасности, предотвращающая публичный доступ к административному интерфейсу.

Административный интерфейс - приложение admin, доступно только с локального хоста если не запущено за веб сервером Apache используя модуль mod_proxy. Если приложение admin обнаруживает работу прокси-сервера, то устанавливается куки сессии для обеспечения безопасности и вход в административный интерфейс работает если связь между браузером и сервером осуществляется по протоколу HTTPS(это еще одна мера безопасности). Все взаимодействия между клиентом и административным интерфейсом всегда должны быть либо локальными, либо зашифрованными; в противном случае злоумышленник может воспользоваться атакой man-in-the middle или replay атакой и выполнить произвольный код на сервере.

административный пароль установлен, web2py запускает веб браузер по адресу:

1
http://127.0.0.1:8000/ 

Если на компьютере нет браузера по умолчанию, откройте веб браузер и введите ссылку http://127.0.0.1:8000

image

Нажав на "administrative interface" вы перейдете в административный интерфейс.

image

Пароль администратора это пароль, который вы устанавливаете во время запуска web2py. Заметьте, администратор в системе только один, поэтому только один пароль администратора. Исходя из соображений безопасности в разработке, пароль запрашивается каждый раз при запуске web2py пока в командной строке во время запуска не задействуется параметр <recycle> в этом отличие механизма аутентификации по сравнению с приложениями web2py.

После входа администратора в web2py, браузер открывает следующую страницу.

image

Эта страница содержит все установленные приложения web2py и позволяет администратору управлять ими. Web2py поставляется с тремя приложениями:

admin
examples
welcome
scaffolding

  • Приложение admin используемое сейчас.
  • Приложение examples с онлайн документацией и репликой на официальный сайт web2py
  • Приложение welcome. Это базовый шаблон для всех других приложений web2py. Так же это приложение запускается когда

пользователь запускает сервер.

appliances

Вы можете скачать в свободном доступе множество приложений web2py [appliances] . Так же пользователи web2py могут написать новое приложение, либо с открытым исходным кодом, либо с закрытым исходным кодом.(подразумевается что приложение будет скомпилировано и запаковано).

На странице приложения admin, вы можете произвести следующие операции:

  • инсталлировать приложение, заполнив форму с правой стороны страницы. В форме необходимо указать имя приложения, место расположения пакета приложения или URL адрес, где расположено приложение, далее нажать кнопку "install".
  • деинсталлировать приложение, нажав кнопку "удалить" напротив соответствующего приложения.
  • создать новое приложение, указав его имя и нажав кнопку "Create"
  • упаковать приложение, нажав соответствующую кнопку. "Pack all"

Скачиваемое приложение это tar файл, содержащий в себе все, включая базу данных. Вам нет необходимости распаковывать этот файл; он автоматически распакуется web2py, когда будет устанавливаться.

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

копирует шаблон "welcome". Используя строительный шаблон "models/db.py" создаётся база данных SQLite, прописывается связь с ней, инициируются сервисы авторизации и различные службы. Так же создается "контроллер/default.py" который отвечает за действия "index", "download", "user", "call". В нашем следующем приложении мы предположим, что эти файлы удалены - создадим приложение с нуля. ----

          • Web2py так же имеет "New application wirard" - "Мастер нового приложения" трубующий подключения к интернету, позволяющий выбрать доступные шаблоны и плагины. Об этом мастере мы

поговорим в следующих главах.

Создание нового просто приложения
index

Для примера мы создадим простое веб приложение, которое отобразит сообщение "Hello from MyApp". Назовем это приложение "myapp". Так же мы добавим счетчик посещений этой страницы.

Создайте новое простое приложение, указав его имя в форме находящейся справа вверху страницы административного интерфейса.

image

После того как вы нажмете кнопку [create](в английской локализации) [создать] (в русской), создастся приложение копирующее встроенное приложение welcome.(От переводчика - далее буду использовать английские названия кнопок и форм поскольку картинки отображающие рабочий процесс выполнены с английской локализации. )

image

Для просмотра нового приложения посетите:

1
 http://127.0.0.1:8000/myapp 

Сейчас вы имеете копию приложения welcome.

Для того что бы изменить приложение нажмите кнопку "Edit" в новом приложении.

Страница EDIT APPLICATION показывает вам, что находится внутри приложения. Каждое приложение web2py состоит из файлов находящихся в следующих категориях:

  • Models: модели представление данных.
  • Controllers: Контроллеры, описывают логику приложения.
  • Views: Отображение, описывают представление данных.
  • Languages: Языки описывают перевод приложения на другие языки.
  • Modules: Модули описывают дополнительные модули на языке Python принадлежащие этому приложению.
  • Static files: статические картинки, файлы CSS [css-w,css-o,css-school] , JavaScript файлы[js-w,js-b] , и др..
  • Plagins: Плагины, группа файлов, спроектированная для совместной работы.

Все организованно согласно шаблону проектирования MVC(model-view-controller). Модель-представление-Управление. Каждая секция на странице редактирования ссылается на соответствующую подпапку в папке приложения.

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

              • Каждый файл в секции ссылается на файл расположенный в подпапке

приложения. Используя административный интерфейс можно производить любые операции с файлом (создание, редактирование, удаление), также можно производить эти операции, используя ваш любимый текстовый редактор. --------

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

Контроллеры содержат в себе логическую схему работы приложения. Каждая ссылка URL связана с вызываемой функцией. По умолчанию создаются два файла : "appadmin.py" и "default.py". appadmin предоставляет интерфейс управления базой данных; пока он нам не нужен. "default.py" - это контроллер, который нам необходимо редактировать, он отвечает за обработку вызываемых ссылок. Отредактируйте содержимое "default.py", а именно функцию

1
2
 def index():
    return "Hello from MyApp" 

На картинке представлен онлайн редактор.

image

Сохраните функцию и посетите вновь созданную страницу.

Когда вы посещаете URL

1
 http://127.0.0.1:8000/myapp/default/index 
вызывается действие index в контроллере default приложения myapp, которое возвращает строку в браузер "Hello from MyApp". Выглядит это следующим образом.

image

Сейчас, отредактируйте функцию "index" написав в нее следующее:

1
2
 def index():
        return dict(message="Hello from MyApp") 

Дополнительно отредактируйте вид "default/index.html" находящийся в категории "Views" и полностью замените существующий код на следующий:

1
2
3
4
5
 <html>
   <head></head>
   <body>
      <h1>{{=message}}</h1>
   </body> </html> 

Архитектура web2py реализована таким образом, действие которое совершает пользователь в данном случае запрашивает страницу index обрабатывается контроллером default.py в котором определена функция index, при этом для отображения страницы index, вызывается отображение default/index.html. Итак, мы видим соответствие - функция контроллера находится в файле default и имеет название def:index - для него вызывается отображение view default/index.html

Функция index возвращает словарь определенный как message. Когда какое либо действие возвращает словарь, web2py возвращает отображение по следующему пути

1
[controller]/[function].[extension]

1
default/index.html
-в этом случае

Если web2py не находит запрашиваемый вид, то использует вид "generic.html" который есть в любом приложении.

            • Если указано расширение отличное от "html"(например "json"), и в файл view "[controller]/[function].json" не найден,

web2py использует общий файл view "generic.json". web2py содержит файлы generic.html, generic.json, generic.xml и generic.rss. эти общие файлы отображений могут быть модифицированы для каждого приложения индивидуально, а так же могут быть добавлены дополнительные файлы отображений.----

              • Общие файлы views это средства разработчика. В конечном продукте каждое действие должно иметь свой собственный файл представления. На самом деле, по умолчанию общие файлы представления активируются только для локального хоста.---
Вы можете указать свой файл отображения response.view = 'default/something.html'

Более подробно об этом читайте в главе 10.

Пройдите сейчас обратно на страницу правки приложения и кликните по названию самого приложения, таким образом, вы вызовете действие index, и увидите следующую html страницу:

image

Для того что бы провести отладку приложения вы всегда можете добавить

1
{{=response.toolbar()}}

в код вашего файла вида. Этот код позволит вам видеть полезную информацию, такую как запрос, ответ и объекты сессии, и список запросов к базе данных а так же время их выполнения. от переводчика: Наверно для того что бы точнее передать суть происходящего необходимо использовать ключевое слово view, что бы указать причастность этого файла именно к уровню отображения. Не пугайтесь иногда я буду использовать слова файл вида, а иногда файл отображения, а иногда шаблон отображения, а иногда воспользуюсь предложением Анатолия ИФК (интернет функция контроллера)

Добавим счетчик посещений

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

Web2py автоматически и прозрачно отслеживает пользователей используя сессии и куки. Для каждого нового пользователя web2py создает сессию и назначает уникальный "session_id". Сессия это контейнер для переменных который располагается на серверной стороне. Уникальный идентификатор посылается браузером используя куки. Когда посетитель запрашивает страницу этого же приложения, браузер посылает куки снова, и соответствующая сессия которая была у пользователя восстанавливается.

Для того что бы использовать сессии отредактируйте контроллер default:

1
2
3
4
5
6
def index():
    if not session.counter:
        session.counter = 1
    else:
        session.counter += 1
    return dict(message="Hello from MyApp", counter=session.counter)

Обратите внимание counter не ключевое слово web2py, однако session ключевое. Мы указываем web2py проверять наличие переменной счетчика (counter) в текущей сессии, и если его нет то создать эту переменную и установить значение переменной равной 1. Если же переменная счетчик присутствует, мы указываем web2py инкрементировать счетчик на 1. В конце мы пропускаем значение счетчика в словарь, который используется в шаблоне представления.

Более компактный код этой же функции:

1
2
3
def index():
    session.counter = (session.counter or 0) + 1
    return dict(message="Hello from MyApp", counter=session.counter)

Отредактируем файл представления view и добавим в него строку, которая отобразит значение счетчика:

1
2
3
4
5
6
7
<html>
   <head></head>
   <body>
      <h1>{{=message}}</h1>
      <h2>Number of visits: {{=counter}}</h2>
   </body>
</html>

Каждый раз когда вы посещаете страницу index вы должны видеть примерно следующую HTML страницу:

image

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

Запоминаем имя посетителя

form
request.vars

Создадим 2 страницы (first и second), на первой странице (first) создадим форму, которая будет спрашивать имя посетителя, и направлять посетителя на вторую страницу (second), которая будет приветствовать посетителя по его имени.

yUML diagram

Напишите нижеследующее действие в контроллер default:

1
2
3
4
5
def first():
    return dict()

def second():
    return dict()

Далее создайте файл отображения "default/first.html" для этого действия и впишите в него следующий код:

1
2
3
4
5
{{extend 'layout.html'}}
What is your name?
<form action="second">
  <input name="visitor_name" />
  <input type="submit" /> </form> 

Небольшое отступление. Для того что бы создать файл перейдите в секцию Views внизу списка файлов заполните поле "create file with filename:", вписав в него следующее "default/first.html". Автоматически откроется встроенный текстовый редактор этого файла, но вы так же можете создать этот файл, перейдя в папку Views и далее в папку default вашего приложения.

Создайте файл представления "default/second.html" для второго действия:

1
2
{{extend 'layout.html'}}
<h1>Hello {{=request.vars.visitor_name}}</h1>

В обоих файлах представления имеется расширение "layout.html" - этот файл отображения поставляется вместе с web2py. Этот шаблон отображается на обоих страницах. Файл шаблона может быть легко отредактирован, поскольку в основном состоит из HTML кода.

Откройте страницу по адресу http://127.0.0.1:8000/myapp/default/first и введите ваше имя

image

Нажав кнопку submit в форме вы получите приветствие:

image

Postbacks - Механизм обратной передачи

redirect
URL
postback

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

Лучший образец программирования это когда отправка данных осуществляется в том же действии, которое сгенерировала форму. В нашем примере это действие first. Итак, действие first должно получить данные, обработать их, сохранить на сервере, и перенаправить посетителя веб страницы на другую страницу "second" которая получит уже сохраненные данные. Этот механизм называется Обратная передача. Поскольку прямой перевод "Обратная передача" не слишком хорош, то для описания этого будет использоваться "Postback механизм"

yUML diagram

Внесите следующие изменения в контроллер default.py

1
2
3
4
5
6
7
8
def first():
    if request.vars.visitor_name:
        session.visitor_name = request.vars.visitor_name
        redirect(URL('second'))
    return dict()

def second():
    return dict()

Далее измените файл отображения "default/first.html":

1
2
3
4
5
6
{{extend 'layout.html'}}
What is your name?
<form> 
  <input name="visitor_name" />
  <input type="submit" />
</form>

на первый взгляд ни чего не изменилось по сравнению с предыдущим примером, но обратите внимание на тег <form> в нем уже нет action="second"

и файл "default/second.html" в нем мы заменим request.vars (полученные переменные) на данные сессии session:

1
2
{{extend 'layout.html'}}
<h1>Hello {{=session.visitor_name or "anonymous"}}</h1>

С точки зрения посетителя поведение остается таким же, как и было в предыдущем примере. Мы пока еще не добавили проверку вводимых данных, но уже сейчас видно, что проверка должна происходить в процедуре first.

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

Заметьте, что если перейти на страницу second без задействования first то на странице отобразится "Hello anonymous" это происходит потому что session.visitor_name возвращает None. Кроме того мы бы могли добавить следующий код внутри функции "second":

1
2
if not request.function=='first' and not session.visitor_name:
    redirect(URL('first'))

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

FORM
INPUT
requires
IS_NOT_EMPTY
accepts

Web2py позволяет генерировать формы (без описаниях их в файлах html) включающие в себя проверку вводимых данных. Вообще в web2py есть следующие сущности (FORM, INPUT, TEXTAREA, and SELECT/OPTION) c похожими именами тегов HTML.

Для примера, один из возможных путей описания first функции:

1
2
3
4
5
6
7
def first():
    form = FORM(INPUT(_name='visitor_name', requires=IS_NOT_EMPTY()),
              INPUT(_type='submit'))
    if form.process().accepted:
        session.visitor_name = form.vars.visitor_name
        redirect(URL('second'))
    return dict(form=form)

В этом коде мы указываем, что форма содержит 2 тега INPUT. В атрибутах указывается имя аргумента указанного с символом подчеркивания в начале. Аргумент requires это не атрибут тега ( видите он не начинается с символа подчеркивания), но он является валидатором - проверкой входящих данных для значений visitor_name.

Лучший код аналогичной формы:

1
2
3
4
5
6
def first():
    form = SQLFORM.factory(Field('visitor_name', requires=IS_NOT_EMPTY()))
    if form.process().accepted:
        session.visitor_name = form.vars.visitor_name
        redirect(URL('second'))
    return dict(form=form)

Объект form может быть легко сериализован в HTML путем встраивания его в файл "default/first.html"

1
2
3
{{extend 'layout.html'}}
What is your name?
{{=form}}

Метод form.process() добавляет проверку и возвращает форму самому себе. Переменная form.accepted устанавливается в True если форма была обработана и прошла проверку. Если форма прошла проверку, то значения переменных сохраняются в сессии и перенаправляются так же. Если же данные в форме (а в данном случае сама форма) не прошли проверку то посетителю отображается та же страница, но содержащая указания ошибок.

image

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

Блог с изображениями

upload

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

Как и ранее создайте новое простое приложение, используя интерфейс администратора и дайте имя этому приложению images:

image

Начнем с создания модели, Модель описывает данные в приложении (картинки их имена и комментарии). Для начала необходимо создать (или отредактировать) файл модели, который мы назовем "db.py". этот файл создается по умолчанию и мы просто заменим его содержимое. К слову файлы моделей и контроллеров имеют расширение ".py" потому что это Python код, а если не указать расширение, то оно подставится автоматически самим web Фреймворком web2py.

Все так же отредактируйте файл "db.py" нажав кнопку "edit" рядом с файлом (помните сейчас мы работаем с моделью представления данных, соответственно файл располагается в секции "Models")

image

Впишите в файл "db.py" следующий код

IS_EMAIL
IS_NOT_EMPTY
IS_IN_DB

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
db = DAL("sqlite://storage.sqlite")

db.define_table('image',
   Field('title', unique=True),
   Field('file', 'upload'),
   format = '%(title)s')

db.define_table('comment',
   Field('image_id', db.image),
   Field('author'),
   Field('email'),
   Field('body', 'text'))

db.image.title.requires = IS_NOT_IN_DB(db, db.image.title)
db.comment.image_id.requires = IS_IN_DB(db, db.image.id, '%(title)s')
db.comment.author.requires = IS_NOT_EMPTY()
db.comment.email.requires = IS_EMAIL()
db.comment.body.requires = IS_NOT_EMPTY()

db.comment.image_id.writable = db.comment.image_id.readable = False

Проанализируем код.

Линия 1 определяет глобальную переменную называемую db которая описывает соединение с базой данных. В этом самом нашем случае это соединение с базой данных SQLite, файл самой базы находится по следующему пути "applications/images/databases/storage.sqlite". Если файл базы данных не существует, то он будет создан. Вы можете конечно поменять имя файла, главное что бы так же было изменено значение переменной db. Главное это удобство запоминания.

Строки 3-5 определяют таблицу "image". define_table это метод объекта db. Первый аргумент "image" - имя определяемой таблицы. Остальные аргументы это поля таблицы. Таблица имеет поля с именами "title" , "file" , а также поле "id" первичный ключ (поле "id" не указано потому что по умолчанию все создаваемые таблицы создаются с этим полем ). Поле "title" это строка. Поле "file" имеет тип "upload"(это специальный тип используемый web2py на уровне Data Abstraction Layer (DAL) для сохранения имени загружаемых файлов). Этот тип "upload" позволяет Web2py знать, как закачивать файлы (используя потоки если они большие), переименовывать их, и сохранять.

Когда таблица определена web2py производит одно из нижеследующих действий:

  • если таблица не существует, то таблица создается;
  • если таблица существует, но не соответствует определению, то таблица меняется. Если поля в существующей таблице имеют другой тип то web2py пытается конвертировать их содержимое;
  • если таблица существует, соответствует определению, то web2py ни чего не делает.

Такое поведение называется миграция. В web2py миграция осуществляется автоматически, но может быть и отключена путем добавления аргумента migrate=False в определении таблицы define_table.

Линия 6 определяет формат строк для таблицы. Определяет как запись должна быть представлена в строке. Аргумент format это так же и функция, которая берет запись и возвращает строку. Например:

1
format=lambda row: row.title

Линия 8-12 определяет другую таблицу с названием "comment" комментарии имеют автора "author", "email" и непосредственно сам текст комментария "body" с типом "text" и поле идентификатор картинки "image_id" с типом, ссылающимся на указатель db.image.

В линии 14 db.image.title представление поля "title" таблицы "image". Атрибут requires позволяет вам задать требования к форме создаваемой web2py. В данном случае поле "title" - должно быть уникальным:

1
IS_NOT_IN_DB(db, db.image.title)

На самом деле это не обязательно, поскольку это делается автоматически после указания Field('title', unique=True)

Объекты представляющие эти ограничения называются валидаторы (проверяльщики значений) . Множество валидаторов может быть сгруппировано в список. Валидаторы работают в порядке их появления. IS_NOT_IN_DB(a, b) это специальный валидатор который проверяет что значение поля b для новых записей не содержится в a Линия 15 проверяет наличие поля "image_id" таблицы "comment" в db.image.id. Что касается базы данных то это уже было определено в описании таблицы "comment". Что касается модели то этой записью мы явно говорим web2py проверять это условие на уровне обработки формы, т.о когда новый комментарий будет написан в форму будет произведена проверка вводимых данных и после этого данные будут отправлены в базу данных. Мы также требуем что бы "image_id" был описан в "title".

Линия 20 показывает что поле "image_id" таблицы "comment" не должно отображаться в форме writable=False и так же не предназначено для чтения readable=False.

Смысл проверок в линиях 15-17 очевиден.

format

Обратите внимание что валидатор

1
db.comment.image_id.requires = IS_IN_DB(db, db.image.id, '%(title)s')

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

1
db.define_table('image', ..., format='%(title)s')

В этой записи формат может быть строкой или функцией, которая берет запись и возвращает форматированную строку.

appadmin

После того как модель определена, и в ней нет ошибок, web2py создает приложение административного интерфейса для управления базой данных. Вы можете получить доступ к управлению базой данных, воспользовавшись ссылкой "database administrator" в интерфейсе администрирования web2py.

1
http://127.0.0.1:8000/images/appadmin

Так выглядит интерфейс управления базой данных:

image

Этот интерфейс называется "appadmin.py" и соотвествует странице "appadmin.html". C настоящего момента мы будем называть этот интерфейс просто appadmin. Итак этот интерфейс позволяет администратору вставлять в базу данных новые записи, редактировать существующие и удалять их, просматривать таблицы, и выполнять joinты.

Когда вы впервые открываете страницу appadmin, файл модели выполняется, и создаются указанные таблицы. DAL web2py выполняет Python код и транслирует его в sql код для базы данных. Просмотреть сформированный sql код создания таблиц вы можете нажав на кнопку "sql.log", находящейся в категории модели. помните что эта ссылка не доступна до тех пор пока не созданы таблицы.

image

Если вы отредактируете файл модели и откроете страницу appadmin снова, то web2py сгенерирует sql код для существующих таблиц. Этот Sql код так же сохранится в "sql.log"

Сейчас вернитесь назад в appadmin и попытайтесь вставить новую запись в таблицу image:

image

Запись db.image.file "upload" web2py трансформирует в форму для загрузки файла. Когда форма отправлена, и файл картинки загружен то web2py переименовывает файл и сохраняет его по безопасному пути в папку "uploads". Имя файла сохраняется в базе данных. Этот процесс реализован для того что бы исключить возможность Traversal атаки. Действительно вы можете сами проверить это перейдя в папку /web2py/applications/images/uploads в ней вы увидите файл примерно с таким именем image.file.a3a1bd1dcf4d14ad.494d4147303133332e6a7067.jpg.

Заметка: каждый поле типов формируется виджетом widget . Виджеты которые используются по умолчанию могут быть заменены.

Когда вы нажимаете по имени таблицы в appadmin. web2py делает выборку всех записей в указанной таблице идентично запросу DAL ниже

1
db.image.id > 0

image

От переводчика: Кода я выполнял этот пункт, во время просмотра всего содержимого таблицы появлялась ошибка, связанная с тем, что в русской локализации "Database administration" не верно выполняется код. Для устранения этой ошибки я изменил предпочитаемый язык на English[en] в браузере. Пользуясь, случаем еще и забегу немножко вперед. В качестве базы данных кроме sqlite еще использовал mysql. Далее кратенько расскажу о настройке DAL на использование mysql. Во время тестирования использовал debian - поэтом дальше команды для этой ОС. apt-get install mysql-server sudo apt-get install python-mysqldb на всякий случай mysql -h localhost -u root -p SELECT USER from mysql.user; CREATE USER testuser@localhost IDENTIFIED BY "testuser"; - создаем пользователя для web2py CREATE DATABASE webtopy; - создаем базу для него GRANT ALL ON webtopy.* TO testuser@testuser; - передаем пользователю все права на созданную базу в фале db.py указываем db = DAL('mysql://testuser:topsecret@localhost:3306/webtopy')

Вы можете написать другой запрос для выбора записей, путем редактирования sql запроса . To edit or delete a single record, click on the record id number. Для того что бы отредактировать одну запись, нажмите на ее идентификатор id. Поскольку у нас есть валидатор IS_IN_DB, то соответствующее поле "image_id" может быть выбрано в выпадающем меню. Каждый пункт выпадающего списка является ключом (db.image.id). Валидаторы это мощные обьекты которые знают как представлять поля, фильтровать значения полей, генерировать ошибки, форматировать значения полей. На картинке ниже видно что произойдет когда вы отправите форму которая не удовлетворяет требованиям валидатора:

image

Аналогичные формы могут быть автоматически сгенерированы не только используя appadmin, но и программно через помощник SQLFORM. Эти формы написаны используя CSS и их можно отредактировать. Every application has its own appadmin; therefore, appadmin itself can be modified without affecting other applications. Каждое приложение имеет свой собственный addadmin, то есть appadmin можно модифицировать как угодно и это не затронет другие приложения. Итак, приложение знает, как сохранять данные, и мы можем видеть данные, имея доступ к ним через appadmin. Сделаем шаг вперед, а именно мы хотим создать:

  • Страницу "index" в которой отображается список всех загруженных картинок, отсортированных по названию.
  • Страницу на которой видна сама картинка и комментарии к ней.
  • Страница на которой можно загрузить, сохранить картинку.

yUML diagram

Отредактируйте контроллер "default.py", замените его содержимое на следующий код:

select
1
2
3
def index():
    images = db().select(db.image.ALL, orderby=db.image.title)
    return dict(images=images)

Это действие возвращает словарь. Данные в словаре интерпретируются с данными для отображения "view" который обрабатывает это действие. Если не будет файла отображения связанного с этим действием то тогда данные передадутся в "generic.html" который по умолчанию присутствует во всех приложениях web2py.

images[0]['title'] or equivalently as images[0].title. Действие index выполняет выбор всех полей (db.image.ALL) из таблицы image, сортируя их по названию db.image.title. Результаты выбора это строки Rows .

Если вы не опишете это действие в файле отображения view то для вывода будет использован "views/generic.html" файл.

image

Поскольку вы не создали отображение для этого действия, то web2py выдает данные как плоская таблица.

Давайте все же создадим файл отображения для действия index. Вернись в интерфейс администрирования, и создайте или отредактируйте файл "default/index.html" поместив в него следующий код:

1
2
3
4
5
6
7
{{extend 'layout.html'}}
<h1>Current Images</h1>
<ul>
{{for image in images:}}
{{=LI(A(image.title, _href=URL("show", args=image.id)))}}
{{pass}}
</ul>

Этот шаблон это чистый HTML код со специальными тегами {{...}}. Включенный в эти фигурные скобки текст это чисто Python код с одной большой оговоркой отступы не имею значение. Блок кода (например цикл for или условие ) начинается с линии кончающейся (:) b заканчивается ключевым словом pass. В некоторых случаях конец блока кода очевиден из контекста в таком случае pass использовать не обязательно.

Линия 5-7 это цикл обхода строк в таблице images и каждая строка будет отображена на странице, используя код:

1
LI(A(image.title, _href=URL('show', args=image.id))

Это теги списка <li>...</li> содержащие теги <a href="...">...</a> которые содержат image.title. Значение ссылок это:

1
URL('show', args=image.id)

То есть ссылка на объект внутри приложения вызвав функции "show" с аргументом к ней args=image.id. LI, A и другие это ключевые слова web2py которые соотвествует HTML тегам. Имя аргументов начинаются с символа подчеркивания (например _href) и соотвествует этим же HTML тегам, но уже без символа подчеркивания. _href это атрибут href, _class это атрибут class и тд.

как пример, следующая запись:

1
{{=LI(A('something', _href=URL('show', args=123))}}

Обработается как:

1
<li><a href="/images/default/show/123">something</a></li>

Итак что же у получилось, перейдите по ссылке:

1
http://127.0.0.1:8000/images/default/index

Откроется страница выглядящая примерно так:

image

Если вы кликните по ссылке рядом с названием картинки вы перейдете по адресу:

1
http://127.0.0.1:8000/images/default/show/1

и получите ошибку, информирующую вас о том что действие "show" в контроллере "default.py" не создано.

итак отредактируйте файл контроллера "default.py" и добавьте в него следующее:

SQLFORM
accepts
response.flash
request.args

response.download
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
def index():
    images = db().select(db.image.ALL, orderby=db.image.title)
    return dict(images=images)

def show():
    image = db(db.image.id==request.args(0)).select().first()
    db.comment.image_id.default = image.id
    form = SQLFORM(db.comment)
    if form.process().accepted:
        response.flash = 'your comment is posted'
    comments = db(db.comment.image_id==image.id).select()
    return dict(image=image, comments=comments, form=form)

def download():
    return response.download(request, db)

В контроллер добавилось два действия: "show" и "download". Функция "show" выбирает картинку по id и все комментарии которые относятся к ней и вызывает отображение "default/show.html" которого, увы, пока нет. The image id referenced by:

1
URL('show', args=image.id)

in "default/index.html", can be accessed as:

request.args(0)

from the "show" action.

Функция "download" оперирует с именем файла, переданным в request.args(0), строит путь к указанному файлу и делает возможным передачу файла назад посетителю. Если файл большой, то организуется потоки без дополнительного выделения памяти.

Обратите внимание на следующие утверждения:

  • Линия 7 . Создание формы SQLFORM для таблицы комментариев db.comment.
  • Линия 8 Установление значений для полей, которые не видимы, в которые посетитель не вписывает данные.
  • линия 9 Оперирование данными переданными в форму(эти данные находятся где ? в request.vars) вместе с текущей сессией(использование сессий позволяет нам избежать двойной отправки одного и того же комментария). Если передаваемые в форму данные прошли проверку то в базу данных добавляется новая запись. ( например проверка на правильность введения email) Это все делается в одной строчке !!
  • линия 10 переход на эту строчку кода происходит только тогда, когда форма проверена и данные отправлены в таблицу базы данныхresponse.flash это переменная web2py, которая отображается на странице и используется для информирования пользователя о его действиях.
  • линия 11 выборка всех комментариев относящихся к текущей картинке.
Функция "download" уже определена в контроллере "default.py" во время создания приложения.

Действие "download" не возвращает словарь данных, то есть нам не нужен файл отображения. А вот действие "show" возвращает словарь, так нам надо создать для него файл отображения "default/show.html".

Создайте этот новый файл и добавьте в него следующий код:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
{{extend 'layout.html'}}
<h1>Image: {{=image.title}}</h1>
<center>
<img width="200px"
     src="{{=URL('download', args=image.file)}}" />
</center>
{{if len(comments):}}
  <h2>Comments</h2><br /><p>
  {{for comment in comments:}}
    <p>{{=comment.author}} says <i>{{=comment.body}}</i></p>
  {{pass}}</p>
{{else:}}
  <h2>No comments posted yet</h2>
{{pass}}
<h2>Post a comment</h2>
{{=form}}

Это файл отображение показывает нам image.file вызвав действие "download" и поместив результат действия в тег <img ... />. Если в таблице комментариев к этому изображению есть комментарий то используя цикл все комментарии отображаются на странице.

вот что примерно видит посетитель.

image

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

Adding CRUD Добавление API CRUD

Web2py Обеспечивает API интерфейс CRUD (Create/Read/Update/Delete) или по русски СоЧтОбнУ(создание/чтение/обновление/удаление) который еще больше упрощает работу с формами. Для использования CRUD необходимо определить следующее в файле "db.py"

1
2
from gluon.tools import Crud
crud = Crud(db)
Эти 2 линии уже включены в строительное приложение. Если вы создадите новое приложение или посмотрите существующее welcome вы найдете их в файле db.py

Обьект crud (тут больше подходит определение сущность) представляет высоко уровневые методы например:

1
form = crud.create(table)

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

1
2
3
4
form = SQLFORM(table)
if form.process().accepted:
    session.flash = '...'
    redirect('...')

Давайте перепишем предыдущую функцию "show" используя CRUD и добавим несколько дополнительных возможностей:

1
2
3
4
5
6
7
8
def show():
    image = db.image(request.args(0)) or redirect(URL('index'))
    db.comment.image_id.default = image.id
    form = crud.create(db.comment,
                       message='your comment is posted',
		       next=URL(args=image.id))
    comments = db(db.comment.image_id==image.id).select()
    return dict(image=image, comments=comments, form=form)
1
db.image(request.args(0)) or redirect(...)

С помощью этой записи извлекаются необходимые данные из таблицы. Если запись в таблице отсутствует, возвращается None и посетитель перенаправляется с помощью кода or redirect(...).

next это аргумент метода crud.create который является ссылкой куда направлять после того как форма была отправлена. message - это аргумент который будет добавлен на страницу после того как форма будет отправлена. Более подробно об этом методе в главе 7.

Добавление Аутентификации

Интерфейс АPI web2py для реализации разграничения доступа довольно сложен и будет рассмотрен в главе 9. Сейчас мы всего лиш ограничим доступ к действию show ( к просмотру картинок) для не аутентифицированных посетителей.

Для ограничения доступа аутентифицированных пользователей нам надо сделать 3 шага. В модели "db.py" нужно добавить:

1
2
3
from gluon.tools import Auth
auth = Auth(db)
auth.define_tables()

в нашем контроллере нужно добавить процедуру:

1
2
def user():
    return dict(form=auth())

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

image

Мы можем декорировать функцию к которой мы хотим ограничить доступ. @[ имя функции] которая должна выполнится до декорируемой функции.

1
2
3
4
5
6
7
8
@auth.requires_login()
def show():
    image = db.image(request.args(0)) or redirect(URL('index'))
    db.comment.image_id.default = image.id
    form = crud.create(db.comment, next=URL(args=image.id),
                     message='your comment is posted')
    comments = db(db.comment.image_id==image.id).select()
    return dict(image=image, comments=comments, form=form)

Все попытки доступа к

1
http://127.0.0.1:8000/images/default/show/[image_id]

требуют авторизации. Если пользователь не авторизован то он перенаправляется на

1
http://127.0.0.1:8000/images/default/user/login

image

Функция user также предоставляет среди прочего следующие действия:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
http://127.0.0.1:8000/images/default/user/logout
http://127.0.0.1:8000/images/default/user/register
http://127.0.0.1:8000/images/default/user/profile
http://127.0.0.1:8000/images/default/user/change_password
http://127.0.0.1:8000/images/default/user/request_reset_password
http://127.0.0.1:8000/images/default/user/retrieve_username
http://127.0.0.1:8000/images/default/user/retrieve_password
http://127.0.0.1:8000/images/default/user/verify_email
http://127.0.0.1:8000/images/default/user/impersonate
http://127.0.0.1:8000/images/default/user/not_authorized

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

Обьект auth и функция user уже имеется в строительном шаблоне. Объект аутентификация может быть очень тонко настроен и может высылать ссылку подтверждения на email, регистрация с использованием CAPTCHA (запрос кода), и другие методы аутентификации с помощью плагинов.

Добавление Smart Grid

Мы можем улучшить интерфейс управления нашим приложением добавив гаджеты SQLFORM.grid и SQLFORM.smartgrid

1
2
3
4
@auth.requires_membership('manager')
def manage():
    grid = SQLFORM.smartgrid(db.image)
    return dict(grid=grid)

и ассоциировав эту функции с файлом отображения "views/default/manage.html"

{{extend 'layout.html'}}
<h2>Management Interface</h2>
{{=grid}}

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

http://127.0.0.1:8000/images/default/manage

теперь вы можете просматривать картинки и использовать функцию поиска:

image

создавать, обновлять удалять картинки и комментарии к ним:

image

Конфигурирование макета

Мы можем отредактировать шаблон используемый по умолчанию "views/layout.html", кстати вы можете отредактировать его не редактируя HTML код, а редактируя таблицу стилей"static/base.css" об этом мы поговорим в главе 5. Можно поменять цвета, колонки, размер, границы, фон и тд не редактируя HTML код. изменить меню различных приложений вы можете редактируя файлы моделей. Шаблон, используемый для построения, устанавливает значения по умолчанию для меню в файле "models/menu.py":

1
2
3
4
5
6
response.title = request.application
response.subtitle = T('customize me!')
response.meta.author = 'you'
response.meta.description = 'describe your app'
response.meta.keywords = 'bla bla bla'
response.menu = [ [ 'Index', False, URL('index') ] ]

Wiki

wiki
RSS
Ajax
XMLRPC

В этом разделе мы построим Wiki, без использования возможностей предоставляемых плагином wiki ( подробнее об этом плагине в главе 12). Посетитель сможет создавать страницы редактировать их, использовать на них поиск ( по названию). Посетитель так же сможет оставлять комментарии ( так же как и в предыдущем приложении), и так же добавим возможность загружать документы. Синтаксис используемых wiki файлов будет Markmin. Дополнительно реализуем функцию поиска используя технологию Ajax, и RSS канал, так же функцию поиска страниц с помощью XML-PRC [xmlrpc].

Следующая диаграмма отображает действия, которые мы хотим реализовать.

yUML diagram

Создайте новое простое приложение и назовите его "mywiki".

The model must contain three tables: page, comment, and document. Both comment and document reference page because they belong to page. A document contains a file field of type upload as in the previous images application. Модель состоит из 3 таблиц: page, comment, document. comment и document ссылаются на page потому что относятся к ней. Document состоит из поля file c типом upload (так же как и в приложении images)

итак наша модель:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
db = DAL('sqlite://storage.sqlite')

from gluon.tools import *
auth = Auth(db)
auth.define_tables()
crud = Crud(db)

db.define_table('page',
    Field('title'),
    Field('body', 'text'),
    Field('created_on', 'datetime', default=request.now),
    Field('created_by', db.auth_user, default=auth.user_id),
    format='%(title)s')

db.define_table('comment',
    Field('page_id', db.page),
    Field('body', 'text'),
    Field('created_on', 'datetime', default=request.now),
    Field('created_by', db.auth_user, default=auth.user_id))

db.define_table('document',
    Field('page_id', db.page),
    Field('name'),
    Field('file', 'upload'),
    Field('created_on', 'datetime', default=request.now),
    Field('created_by', db.auth_user, default=auth.user_id),
    format='%(name)s')

db.page.title.requires = IS_NOT_IN_DB(db, 'page.title')
db.page.body.requires = IS_NOT_EMPTY()
db.page.created_by.readable = db.page.created_by.writable = False
db.page.created_on.readable = db.page.created_on.writable = False

db.comment.body.requires = IS_NOT_EMPTY()
db.comment.page_id.readable = db.comment.page_id.writable = False
db.comment.created_by.readable = db.comment.created_by.writable = False
db.comment.created_on.readable = db.comment.created_on.writable = False

db.document.name.requires = IS_NOT_IN_DB(db, 'document.name')
db.document.page_id.readable = db.document.page_id.writable = False
db.document.created_by.readable = db.document.created_by.writable = False
db.document.created_on.readable = db.document.created_on.writable = False

Отредактируете контроллер "default.py" и внесите следующие действия:

  • index: Просмотр всех wiki страниц
  • create: Добавление wiki страницы
  • show: Просмотр wiki страницы и комментариев к ней, а так же возможность добавить комментарий
  • edit: редактирование существующей страницы
  • documents: управление документами, прикрепленными к странице
  • download: загрузка документов( так же как в приложении images)
  • search: отображение окна поиска, используя Ajax callback, возвращение всех удовлетворяющих условий названий.
  • callback: Ajax функция, возвращающая HTML код встроенный в страницу поиска.

ниже листинг контроллера "default.py":

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
def index():
     """ this controller returns a dictionary rendered by the view
         it lists all wiki pages
     >>> index().has_key('pages')
     True
     """
     pages = db().select(db.page.id,db.page.title,orderby=db.page.title)
     return dict(pages=pages)

@auth.requires_login()
def create():
     "creates a new empty wiki page"
     form = crud.create(db.page, next=URL('index'))
     return dict(form=form)

def show():
     "shows a wiki page"
     this_page = db.page(request.args(0)) or redirect(URL('index'))
     db.comment.page_id.default = this_page.id
     form = crud.create(db.comment) if auth.user else None
     pagecomments = db(db.comment.page_id==this_page.id).select()
     return dict(page=this_page, comments=pagecomments, form=form)

@auth.requires_login()
def edit():
     "edit an existing wiki page"
     this_page = db.page(request.args(0)) or redirect(URL('index'))
     form = crud.update(db.page, this_page,
                        next=URL('show',args=request.args))
     return dict(form=form)

@auth.requires_login()
def documents():
     "browser, edit all documents attached to a certain page"
     page = db.page(request.args(0)) or redirect(URL('index'))
     db.document.page_id.default = page.id
     db.document.page_id.writable = False
     grid = SQLFORM.grid(db.document.page_id==page.id,args=[page.id])
     return dict(page=page, grid=grid)

def user():
     return dict(form=auth())

def download():
     "allows downloading of documents"
     return response.download(request, db)

def search():
     "an ajax wiki search page"
     return dict(form=FORM(INPUT(_id='keyword',_name='keyword',
              _onkeyup="ajax('callback', ['keyword'], 'target');")),
              target_div=DIV(_id='target'))

def callback():
     "an ajax callback that returns a <ul> of links to wiki pages"
     query = db.page.title.contains(request.vars.keyword)
     pages = db(query).select(orderby=db.page.title)
     links = [A(p.title, _href=URL('show',args=p.id)) for p in pages]
     return UL(*links)

линии 2-6 всего лишь комментарии процедуры index. 4-5 внутри комментария интерпретируется Python как тестовый код (doctest). Тесты могут быть запущены используя административный интерфейс. В нашем случае тест проверят, что процедура index выполняется без ошибок

Линии 18, 27, 35 попытка получить запись page с id указанным в request.args(0).

линии 13, 20 определение и обработка создания форм для новых страниц и новых комментариев.

Линия 28 определение и обработка обновлений для wiki страниц.

Линия 38 создание обьекта сетка grid, который позволит просматривать и обновлять комментарии к страницам.

Некоторое магическое действо происходит на строке 51. Атрибут onkeyup в теге INPUT "keyword". Каждый раз когда посетитель отпускает клавишу выполняется JavaScript код внутри атрибута onkeyup, на клиентской стороне. Ниже этот код:

1
ajax('callback', ['keyword'], 'target');

ajax - это JavaScript функция, определенная в файле "web2py.js" который включен по умолчанию в макет "layout.html". Этот скрипт берет 3 параметра: URL действия для синхронного callback, список идентификаторов посылаемых в callback (["keyword"]), ID того куда необходимо вставить ("target"). Как только вы выписываете что-нибудь в поле поиска и отпускаете клавишу, скрипт на клиентской стороне отсылает серверу содержимое поля 'keyword' и затем сервер отвечает данными которые встраиваются в HTML страницу ( адрес страницы передается в 'target'). Тег 'target' это объект DIV определенный в строке 52, он должен быть определен в шаблоне отображения. ниже указа код файла вида view "default/create.html":

1
2
3
{{extend 'layout.html'}}
<h1>Create new wiki page</h1>
{{=form}}

Если вы посетите страницу create вы увидите следующее:

image

Код страницы вида "default/index.html":

1
2
3
4
5
6
7
{{extend 'layout.html'}}
<h1>Available wiki pages</h1>
[ {{=A('search', _href=URL('search'))}} ]<br />
<ul>{{for page in pages:}}
     {{=LI(A(page.title, _href=URL('show', args=page.id)))}}
{{pass}}</ul>
[ {{=A('create page', _href=URL('create'))}} ]

Отображаемая страница:

image

код файла "default/show.html":

markdown
MARKMIN

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
{{extend 'layout.html'}}
<h1>{{=page.title}}</h1>
[ {{=A('edit', _href=URL('edit', args=request.args))}}
| {{=A('documents', _href=URL('documents', args=request.args))}} ]<br />
{{=MARKMIN(page.body)}}
<h2>Comments</h2>
{{for comment in comments:}}
  <p>{{=db.auth_user[comment.created_by].first_name}} on {{=comment.created_on}}
          says <I>{{=comment.body}}</i></p>
{{pass}}
<h2>Post a comment</h2>
{{=form}}

Если вы хотите использовать синтаксис Markdown:

1
from gluon.contrib.markdown import WIKI

и используйте WIKI с помощником MARKMIN. Альтернатива это чистый HTML синтаксис. В таком случае замените:

1
{{=MARKMIN(page.body)}}

на:

1
{{=XML(page.body)}}
sanitize

(so that the XML does not get escaped, as by default web2py behavior).

This can be done better with:

1
{{=XML(page.body, sanitize=True)}}

By setting sanitize=True, you tell web2py to escape unsafe XML tags such as "<script>", and thus prevent XSS vulnerabilities. Now if, from the index page, you click on a page title, you can see the page that you have created:

image

Код вида "default/edit.html":

1
2
3
4
{{extend 'layout.html'}}
<h1>Edit wiki page</h1>
[ {{=A('show', _href=URL('show', args=request.args))}} ]<br />
{{=form}}

как вы видите страница создания и редактирования полностью идентичны. Here is the code for the view "default/documents.html": Код файла вида "default/documents.html"

1
2
3
4
5
{{extend 'layout.html'}}
<h1>Documents for page: {{=page.title}}</h1>
[ {{=A('show', _href=URL('show', args=request.args))}} ]<br />
<h2>Documents</h2>
{{=grid}}

Теперь если вы из страницы "show" кликните по ссылке documents, вы сможете управлять документами страницы.

image

Код файла "default/search.html":

1
2
3
4
{{extend 'layout.html'}}
<h1>Search wiki pages</h1>
[ {{=A('listall', _href=URL('index'))}}]<br />
{{=form}}<br />{{=target_div}}

который создает следующую Ajax поисковую форму:

image

Вы можете попробовать callback действие в деле нажав первую букву уже добавленной вами страницы:

1
http://127.0.0.1:8000/mywiki/default/callback?keyword=wiki

посмотрите на исходный код страницы, вы увидите html код.

1
<ul><li><a href="/mywiki/default/show/4">I made a Wiki</a></li></ul>
rss

Генерирование RSS канала используя web2py очень легко достаточно включить gluon.contrib.rss2 запись в контроллер default:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
def news():
    "generates rss feed form the wiki pages"
    reponse.generic_patterns = ['.rss']
    pages = db().select(db.page.ALL, orderby=db.page.title)
    return dict(
       title = 'mywiki rss feed',
       link = 'http://127.0.0.1:8000/mywiki/default/index',
       description = 'mywiki news',
       created_on = request.now,
       items = [
          dict(title = row.title,
               link = URL('show', args=row.id),
               description = MARKMIN(row.body).xml(),
               created_on = row.created_on
               ) for row in pages])

Когда вы посетите страницу:

1
http://127.0.0.1:8000/mywiki/default/news.rss

вы увидите канал.

image

XMLRPC

Плюс ко всему добавим XML-RPC обработчик, позволяющий нам искать wiki страницы программно:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
service = Service()

@service.xmlrpc
def find_by(keyword):
     "finds pages that contain keyword for XML-RPC"
     return db(db.page.title.contains(keyword)).select().as_list()

def call():
    "exposes all registered services, including XML-RPC"
    return service()

В этос случаи find_by. find_by это не действие ( потому что используется как аргумент). Обработчик XML-RPC запрашивает данные из базы данных и получает список ответов .response.

Ниже пример как вы можете получить доступ к обработчику XML-RPC из внешней Python программы.

1
2
3
4
5
>>> import xmlrpclib
>>> server = xmlrpclib.ServerProxy(
    'http://127.0.0.1:8000/mywiki/default/call/xmlrpc')
>>> for item in server.find_by('wiki'):
        print item['created_on'], item['title']

Обработчик может быть доступен из множества других языков программирования, которые понимают XML-RPC, включая C, C++, C# и Java.

Форматы даты date, datetime,time

Есть 3 различных представления для каждого из типов полей date, datetime and time:

  • представление базы данных
  • внутреннее web2py представление
  • строковое представление в формах и таблицах

Представление базы данных это внутреннее результат, который не влияет на код. На уровне обработки web2py они сохраняются как объекты datetime.date, datetime.datetime и datetime.time, объектами можно манипулировать следующим образом

for page in db(db.page).select():
    print page.title, page.day, page.month, page.year

Когда даты конвертируются в строки для форм они конвертируются, используя ISO представление

%Y-%m-%d %H:%M:%S

Это интернациональное представление дат, но вы можете поменять этот тип, используя страницы языков, где можно поменять формат дат.

%m/%b/%Y %H:%M:%S
Помните что по умолчанию используется Английский язык и он не использует фалы языков , если вы хотите интернационализировать проект вам нужно создать файл языка ( все так же используя интерфейс администрирования)
T.current_languages = ['null']

Больше о функциональности админки

admin

Административный интерфейс обеспечивает дополнительные функции, которые кратко мы опишем.

Главная страница

site

Страница отображает список установленных приложений. На ней отображаются 2 формы внизу

Первая позволяет создавать новые приложения, указав их имя

Instant Press

Вторая форма позволяет загрузить уже существующее приложение, указав путь к локальному файлу или введя ссылку. Когда вы скачиваете новое приложение вы должны указать имя для него. Это может быть оригинальное имя или отличное имя (различные имена позволяют использовать разные версии одного и того же приложения) Попробуйте закачать CMS от Мартина Мулона.

1
http://code.google.com/p/instant-press/
Файлы web2py это пакеты с расширением .w2p. Это обычные архивы затареные и за гзипованные. web2py использует расширение w2p а не tgz это сделано для того что бы браузеры не разархивировали эти архивы по умолчанию. архивы можно распаковать руками используя команду tar -zxvf если это необходимо

image

После того как файл скачан web2pe показывает контрольную сумму MD5 что бы вы могли проверить не поврежден ли файл во время скачивания. Нажмите по имени InstantPress, что бы запустить приложение.

image

Более подробно об этой Instant Press CMS вы можете узнать, посетив следующую ссылку:

http://code.google.com/p/instant-press/

Для каждого установленного приложения доступны следующие действия:

  • Де инсталлирование приложения.
  • переход на страницу about
  • переход на страницу редактирования приложения edit
  • переход на страницу ошибок errors
  • удаление временных файлов ( файлы сессий, ошибок, кэша )
  • упаковка всего приложения. ( Архивация всего приложения вместе с скомпилированными файлами, рекомендуем вам перед этой процедурой удалить все временные файлы)
  • компиляция приложения. Если приложение не содержит ошибок то создается byte ориентированный код всех моделей, контроллеров, видов. Компиляция как таковая позволяет коду выполняться быстрее поскольку не нужно заново формировать различные шаблоны списки и тд.
  • запаковать скомпилированное приложение. эта опция позволяет запаковать только скомпилированные файлы без файлов исходного кода.- удалить компилированные файлы. ясно из названия. Отметим лиш если вы используете приложение без исходных кодов, то эта процедура может привести к тому, что приложение вообще перестанет работать.
admin.py
Весь функционал доступный на страницах администрирования также доступен и программно через интерфейс API определенный в модуле gluon/admin.py. Все очень просто откройте Python шел и импортируйте этот модуль.

about

about
license

Страница about позволяет ввести описание приложения и лицензию распространения. Эта информация соотвественно описана в файлах ABOUT и LICENSE находящихся в папке самого приложения.

image

Можно использовать MARKMIN, или gluon.contrib.markdown.WIKI синтаксис для этих файлов.

edit

EDIT

А вы уже использовали эту страницу в этой главе. Сейчас мы всего лишь остановимся на дополнительном функционале страницы.

  • нажав на любой файл вы увидите содержимое файла в редакторе с подсветкой.
  • естественно если вы нажмете на кнопку правка то вы перейдете в интерфейс редактирования файла.
  • нажав удалить вы навсегда удалите файл.
  • нажав тест вы запустите внутреннюю процедуру web2py тестов.
  • вы можете добавить файл языка.

ниже следующее что вы увидите нажав тест приложения welcome(кнопка находится в секции контроллеры)

image

Ниже доступные языки для приложения welcome

image

Ниже способ с помощью которого вы можете отредактировать файл языка. На картинке перевод для итальянского языка.

image

shell Оболочка shell

Если вы нажмете кнопку "shell" ( она находится в секции контроллеры) web2py откроет web оболочку языка Python.

image

crontab Выполнение задач по расписанию

Также в секции контроллеры есть кнопка "crontab", нажав которую откроется web текстовый редактор куда можно внести действия которые необходимо произвести в определенное время или с периодом в какое то вермя. Синтаксис этого файлы очень похож на линуксовый Crontab но на самом деле не являющийся линуксовым сервисом cron. Для того что бы задание было выполнилось достаточно что бы работал web2py.Более подробно в следующих главах.

Ошибки

errors

Когда вы будете программировать web2py вы неизбежно допустите ошибки, что бы их локализовать есть 2 пути: 1) создание тестов тех функций которые нужно анализировать . 2) когда ошибка возникнет, будет сформирован отчет об ошибке, который в свою очередь сохранится в файле лога web2py.

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

1
2
3
4
def index():
    images = db().select(db.image.ALL,orderby=db.image.title)
    1/0
    return dict(images=images)

Когда вы посетите страницу index вы получите следующую ошибку:

image

Только администратор может получить доступ к отчету ошибок:

image

Отчет содержит следующую информацию: обратную трассировку, содержимое файлов вызвавших ошибку, а так же полной список системных переменных, содержимое запроса, сессии.

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

Список всех ошибок на странице errors:

image

Система контроля версийMercurial

Mercurial

Вы можете установить систему контроля версий Mercurial:

1
easy_install mercurial

Когда в административном интерфейсе отображается еще одно меню называемое "mercurial" - это означает что если система контроля версий установлена то web2py автоматически создает локальный репозиторий. Нажав кнопку "commit" вы автоматически внесете в разрабатываемый проект и система контроля версий запишет все изменения проделанные вами. Каждое приложение будет иметь скрытую папку ".hg" содержащую данные для работы системы контроля версий Mercurial.

Веб интерфейс Mercurial не позволит вам смотреть все предыдущие комиты и различия в файлах. Вообще мы рекомендуем для использования системы контроля версий использовать специализированное ПО для этих целей.

images

Более подробно о системе контроля версий читайте по следующему адресу.

http://mercurial.selenic.com/

Вообще то главный разработчик web2py уже давно использует систему контроля версий Git.

Мастер создания приложений (Экспериментальный)

интерфейс администрирования имеет мастер создания приложений.

image

Мастер шаг за шагом создает приложение:

  • 1 шаг выбор имени приложения
  • 2 шаг настройка приложения и выбор необходимых плагинов к нему
  • 3 шаг построение моделей
  • 4 окончательное редактирование перед редактированием.

на картинке ниже отображен 2 й шаг этого процесса

image

ВЫ можете выбрать в выпадающем списке макет шаблона. Необходимые плагины.

последующие шаги достаточно просты для понимания.

Мастер работает без нареканий, но он все так же называется экспериментальным experimental feature по двум причинам:

  • Приложения создаваемые мастером. не могут быть повторно модифицированы мастером.
  • Интерфейс мастера постоянно меняется для добавления новых возможностей и облегчения визуальной разработки.

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

Конфигурирование интерфейса администратора

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

http://127.0.0.1:8000/admin/default/edit/admin/models/0.py

Обратите внимание приложение admin используется для редактирования самого себя. На самом деле admin это тоже приложение web2py

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

GAE_APPCFG = os.path.abspath(os.path.join('/usr/local/bin/appcfg.py'))

Это указание на путь "appcfg.py", который поставляется вместе с Google App Engine SDK. Если вы используете этот SDP вам вероятно понадобится поменять некоторые параметры, что бы разворачивать ваши приложения на GAE.

DEMO_MODE

Вы можете установить демо режим для административного интерфейса web2py:

DEMO_MODE = True
FILTER_APPS = ['welcome']

Только те приложения которые перечислены в фильтре будут доступны в режиме только для чтения.

MULTI_USER_MODE
virtual laboratory

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

MULTI_USER_MODE = True

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

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

Больше о appadmin

appadmin

appadmin не расчитанно для публичной работы, оно спроектировано только как помощь вам в доступе к базе данных. appadmin состоит из двух файлов "appadmin.py" и его отображения "appadmin.html", которые используются для всех действий в контроллере.

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

appadmin показывает доступные базы данных и какие таблицы уже существуют в базе. Вы можете вставлять записи в таблицы, просматривать записи каждой таблицы. appadmin одномоментно показывает 100 записей.

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

Для обновления записей, введите SQL запрос в поле ввода запроса:

1
title = 'test'

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

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

appadmin так же позволяет делать joinты SQL фильтра содержащие условие, включения двух и более таблиц. Для примера попробуйте сделать следующее:

1
db.image.id == db.comment.image_id

Web2py использует уровень абстракции базы данных DAL, и знает что запрос связывает две таблицы; следовательно обе таблицы выбираются с помощью INNER JOIN. Ниже результат запроса:

image

Если вы кликните по полю с номером id вы перейдете в страницу редактирования записи.

Вы не можете обновлять или удалять записи выбранные с помощью join запроса, потому что они объединяют в себе множество таблиц. Это было бы двусмысленно.

В дополнении к возможностям администрирования базы данных, appadmin так же позволяет вам видеть детали содержимого приложения его временные файлыcache (at /yourapp/appadmin/ccache), содержимое ответов response и содержимое объектов сессии session objects (at /yourapp/appadmin/state).

appadmin заменяет response.menu на свое собственное меню. Если шаблон вашего приложения layout не генерирует меню используя response.menu тогда вы не увидите меню на странице appadmin. В таком случаи вы можете модифицировать файл appadmin.html и добавить в него {{=MENU(response.menu)}} , что бы видеть меню.
 top