= Учебник - часть 1 = <> == Вступление == В главе [[Документации/Bluebream/Bluebream-Первые-Шаги|Первые шаги]], был описан процесс установки !BlueBream и создания проекта из шаблона '''bluebream'''. В этом учебнике, вы научитесь создавать простое приложение '''коллектор заявок'''. Данная серия уроков поможет ближе ознакомится с принципами !BlueBream. Вот несколько функций будущего приложения: * В один накопитель можно добавить сколько угодно заявок. * Каждая новая заявка должен иметь описание и один начальный комментарий. * К заявке можно добавлять комментарии. Это первая часть обучения. После завершения этой главы, вы должны уметь: * Понимать структуру директорий проекта. * Использовать и настраивать Buildout. * Создавать контент-объекты (content objects) и интерфейсы. * Использовать инструмент для генерации форм (''zope.formlib''). {{{#!wiki note Примеры, которые предлагаются для изучения в документации можно загрузить [[http://download.zope.org/bluebream/examples/ticketcollector-1.0.0.tar.bz2|отсюда]]. Исходники доступны на разных этапах, соответственно разделам. * Этап 1 : разделы от 5.2 до 5.7 * Этап 2 : раздел 5.8 * Этап 3 : раздел 5.9 * Этап 4 : раздел 6.2 * Этап 5 : раздел 6.3 * Этап 6 : разделы 6.4 & 6.5 }}} == Создание нового проекта == === Использование шаблона проекта bluebream === В этом разделе мы будем создавать структуру папок для нашего приложения. Я предполагаю, что вы уже установили bluebream, используя команду ''easy_install bluebream'', как упоминалось в главе ''Первые шаги''. Давайте назовем приложение '''ticketcollector''', и создадим Python пакет с именем ''tc.main''. Итак, мы создали проект с именем '''ticketcollector''', пакетом пространства имен ''tc'' и под-пакетом '''main'''. Давайте создадим структуру папок для нашего приложения: {{{#!highlight bash $ paster create -t bluebream Selected and implied templates: bluebream#bluebream A BlueBream project, base template Enter project name: ticketcollector Variables: egg: ticketcollector package: ticketcollector project: ticketcollector Enter python_package (Main Python package (with namespace, if any)) ['ticketcollector']: tc.main Enter interpreter (Name of custom Python interpreter) ['breampy']: Enter version (Version (like 0.1)) ['0.1']: Enter description (One-line description of the package) ['']: Ticket Collector Enter long_description (Multi-line description (in reST)) ['']: An issue tracking application Enter keywords (Space-separated keywords/tags) ['']: Enter author (Author name) ['']: Baiju M Enter author_email (Author email) ['']: baiju@example.com Enter url (URL of homepage) ['']: Enter license_name (License name) ['']: ZPL Enter zip_safe (True/False: if the package can be distributed as a .zip file) [False]: Creating template bluebream Creating directory ./ticketcollector Your project has been created! Now, you want to: 1) put the generated files under version control 2) run: python boostrap.py 3) run: ./bin/buildout }}} Как видно из примера выше, мы предоставили почти всю информацию о проекте. Значения, которые были указаны, можно поменять позже. Это сделать очень просто; все, что трудно изменить позже - это имена пакетов, потому что на них позже может быть много ссылок в коде. === Организация нового пакета === Если вы перейдете в папку ''ticketcollector'', вы увидите несколько папок и файлов: {{{ jack@computer:/projects/ticketcollector$ ls -CF bootstrap.py debug.ini etc/ src/ versions.cfg buildout.cfg deploy.ini setup.py var/ }}} Когда структура проекта готова, вы можете добавить его в систему контроля версий. Вам не следует добавлять папку ''src/ticketcollector.egg-info'', так как она автоматически создается с помощью '''setuptools'''. Вот пример с использованием '''bzr''': {{{ jack@computer:/projects/ticketcollector$ rm -fr src/ticketcollector.egg-info/ jack@computer:/projects/ticketcollector$ bzr init Created a standalone tree (format: 2a) jack@computer:/projects/ticketcollector$ bzr add * adding bootstrap.py adding buildout.cfg adding debug.ini ... jack@computer:/projects/ticketcollector$ bzr ci -m "Initial import" Committing to: /projects/ticketcollector/ added bootstrap.py added buildout.cfg ... Committed revision 1. }}} Добавление проекта в систему контроля версий необязательный, но рекомендуемый шаг. У вас теперь есть правильный исходный код, который, после построения, приобретет вид рабочего приложения. Проект теперь полностью независим, от дистрибутива bluebream, который только помогает добраться до этой точки развития. Теперь проект содержит все необходимое для установки зависимостей из Интернета и настройки приложения. === Генерация (bootstrapping) проекта === Следующий шаг - установка Buildout. Цель Buildout - автоматизировать сборку Python приложений из исходников. Единственное требование для Buildout - это Python. !BlueBream предоставляет скрипт генерации для установки !Buildout и настройки проекта для его запуска. Этот скрипт называется ''bootstrap.py'' и делает следующие вещи: * Загружает и устанавливает дистрибутив '''distribute''' с PyPI, который содержит внутри пакет '''setuptools'''. * Загружает и устанавливает дистрибутив ''zc.buildout'' c PyPI. * Создает папки проекта :- bin/, eggs/, parts/, develop-eggs/. * Создает скрипт внутри папки '''bin''' с именем '''buildout'''. Когда вы запускаете ''bootstrap.py'', вы видите, что он создает несколько папок и скрипт ''bin/buildout'', как было упомянуто раньше: {{{ jack@computer:/projects/ticketcollector$ python bootstrap.py Creating directory '/projects/ticketcollector/bin'. Creating directory '/projects/ticketcollector/parts'. Creating directory '/projects/ticketcollector/develop-eggs'. Creating directory '/projects/ticketcollector/eggs'. Generated script '/projects/ticketcollector/bin/buildout'. }}} * Папка ''bin'' - место, куда Buildout устанавливает все выполнимые скрипты. * Папка ''eggs'' - место, куда Buildout устанавливает яйца. * Папка ''parts'', куда Buildout сохраняет весь свой вывод. Buildout ожидает, что в этой папке ничего не будет изменятся со стороны разработчика. * Папка ''develop-eggs'' - место, куда Buildout сохраняет ссылки на все локально разработанные яйца. === Настройка Buildout === После генерации проекта вы можете собрать приложение. Все шаги, которые на этот момент уже сделаны, делаются всего один раз для нового проекта, но запуск сборщика необходим при каждом изменение его конфигурации. Теперь вы готовы запустить ''bin/buildout'' для того, чтобы собрать приложение, но перед этим следует взглянуть на содержимое файла ''buildout.cfg'': {{{#!highlight ini [buildout] develop = . extends = versions.cfg parts = app test [app] recipe = zc.recipe.egg eggs = ticketcollector z3c.evalexception>=2.0 Paste PasteScript PasteDeploy interpreter = breampy [test] recipe = zc.recipe.testrunner eggs = ticketcollector }}} Конфигурация сборщика разделена на несколько секций, называемых частями. Главная часть - '''[buildout]''', является первой частью в листинге выше. Каждая часть, кроме '''[buildout]''', обрабатывается подключаемым механизмом системы Buildout, который называется рецепт. '''[buildout]''' обрабатывается как исключительный случай, так как содержит общие настройки. Давайте рассмотрим '''[buildout]''': {{{#!highlight ini [buildout] develop = . extends = versions.cfg parts = app test }}} Первая опция (develop) указывает сборщику, что текущая папка - папка с исходниками дистрибутива, то есть содержит файл ''setup.py''. Buildout исследует ''setup.py'' и создаст ссылку на яйцо в папке ''develop-eggs''. Ссылочный файл должен содержать путь к пакету Python. Таким образом сборщик удостоверится, что пакеты будут всегда доступны для импорта. Значение опции '''develop''' может быть относительным путем, как указано выше, или абсолютным путем некой папки. Также можно использовать многострочные значения для указания разных путей. Опция '''extends''' указывает сборщику включать полное содержимое файла ''versions.cfg'' в качестве части конфигурации. ''versions.cfg'' - другой файл настроек Buildout такого же формата, как и ''buildout.cfg''. Он содержит номера релизов и другие зависимости. Вы можете устанавливать многострочные значения опции '''extends''' для включения нескольких файлов настроек. Опция '''parts''' содержит список всех частей, которые должны обрабатываться системой Buildout. Buildout ожидает, что на каждую часть конфигурационного файла существует рецепт. Теперь рассмотрим часть '''app''': {{{#!highlight ini [app] recipe = zc.recipe.egg eggs = ticketcollector z3c.evalexception>=2.0 Paste PasteScript PasteDeploy interpreter = breampy }}} Эта часть заботится о всех яйцах (eggs), которые необходимы для работы приложения. Пакет ''zc.recipe.egg'' - это расширенный рецепт системы Buildout, содержащий множество функций для работы с яйцами. Большинство зависимостей будут связаны с яйцом главного приложения. Опция '''eggs''' содержит список всех яиц. Первое яйцо, '''ticketcollector''' - это главное локально разрабатываемое яйцо. Последняя опция - '''interpreter''' определяет имя интерпретатора, который создал эту часть. Интерпретатор содержит пути ко всех яйцам и их зависимостям, которые содержатся в списке '''eggs''', так что вы можете импортировать любой модуль, который присутствует в списке зависимостей. Последняя часть создает загрузчик тестов: {{{#!highlight ini [test] recipe = zc.recipe.testrunner eggs = ticketcollector }}} Рецепт '''testrunner''' создает загрузчик тестов, используя модуль ''zope.testing''. Единственная необязательная опция - '''eggs''', в которой вы указываете список яиц. === Сборка проекта === Теперь вы можете выполнить команду '''bin/buildout'''. Ее выполнение займет некоторое время, так как необходимо загрузить все пакеты с !PyPI. Когда вы запустите сборщик, он выдаст вам примерно следующее: {{{#!highlight bash jack@computer:/projects/ticketcollector$ ./bin/buildout Develop: '/projects/ticketcollector/.' Installing app. Generated script '/projects/ticketcollector/bin/paster'. Generated interpreter '/projects/ticketcollector/bin/breampy'. Installing zope_conf. Installing test. Generated script '/projects/ticketcollector/bin/test'. }}} В этом примере все яйца уже находятся в папке ''eggs''. Если их там нет, они будут автоматически загружены и установлены. Сборщик также создает несколько скриптов в папке ''bin'': * Команда '''paster''' используется для запуска веб сервера. * Команда '''breampy''' предоставляет интерпретатор Python, который содержит все яйца, доступные в проекте. * Команда '''test''' используется для запуска тестов. Теперь у нас есть базовое дерево проекта, на основе которого мы продолжим строить наше приложение. == Настройка PasteDeploy == !BlueBream использует WSGI для запуска сервера путем использования !PasteDeploy. В !PasteDeploy есть два файла настроек: один для внедрения (''deploy.ini''), другой для разработки (''debug.ini''). Давайте подробнее рассмотрим содержимое ''debug.ini'': {{{#!highlight ini [app:main] use = egg:ticketcollector [server:main] use = egg:Paste#http host = 127.0.0.1 port = 8080 [DEFAULT] # set the name of the zope.conf file zope_conf = %(here)s/etc/zope.conf }}} Сначала - раздел '''[app:main]''': {{{#!highlight ini [app:main] use = egg:ticketcollector }}} Раздел '''[app:main]''' определяет, какое яйцо следует использовать. !PasteDeploy ожидает, что точка входа ''paste.app_factory'' будет определена в яйце. Если вы обратитесь к файлу ''setup.py'', вы увидите, что она определяется следующим образом: {{{#!highlight ini [paste.app_factory] main = tc.main.startup:application_factory }}} Имя отчки входа должно быть '''main'''. В противном случае, оно должно явно указыватся в файле настроек (''debug.ini'' и ''deploy.ini''). Например, если определение следующее: {{{#!highlight ini [paste.app_factory] testapp = tc.main.startup:application_factory }}} Конфигурацию !PasteDeploy следует изменить к следующему виду: {{{#!highlight ini [app:main] use = egg:ticketcollector#testapp }}} Следующий раздел ('''[server:main]''') определяет WSGI сервер: {{{#!highlight ini [server:main] use = egg:Paste#http host = 127.0.0.1 port = 8080 }}} В этом разделе вы можете изменить имя хоста, порт и сервер самостоятельно. В случае, если вы используете любой другой WSGI сервер, он должен быть включен в список зависимостей в настройках Buildoout. Последний раздел ('''[DEFAULT]''') - место, где вы определяете значения по умолчанию: {{{#!highlight ini [DEFAULT] # укажите путь к файлу ''zope.conf'' zope_conf = %(here)s/etc/zope.conf }}} WSGI приложение, которое определено в ''tc.main.startup'' ожидает определения опции '''zope_conf''' в разделе '''[DEFAULT]'''. Следовательно, эта опция необязательная. Она определяет путь к главному конфигурационному файлу zope. Мы рассмотрим конфигурационный файл zope более детально в следующем разделе. Файл ''debug.ini'' содержит полезные настройки для отладки приложений: {{{#!highlight ini [loggers] keys = root, wsgi [handlers] keys = console, accesslog [formatters] keys = generic, accesslog [formatter_generic] format = %(asctime)s %(levelname)s [%(name)s] %(message)s [formatter_accesslog] format = %(message)s [handler_console] class = StreamHandler args = (sys.stderr,) level = ERROR formatter = generic [handler_accesslog] class = FileHandler args = (os.path.join('var', 'log', 'access.log'), 'a') level = INFO formatter = accesslog [logger_root] level = INFO handlers = console [logger_wsgi] level = INFO handlers = accesslog qualname = wsgi propagate = 0 [filter:translogger] use = egg:Paste#translogger setup_console_handler = False logger_name = wsgi [filter-app:main] # Change the last part from 'ajax' to 'pdb' for a post-mortem debugger # on the console: use = egg:z3c.evalexception#ajax next = zope [app:zope] use = egg:ticketcollector filter-with = translogger [server:main] use = egg:Paste#http host = 127.0.0.1 port = 8080 [DEFAULT] # set the name of the debug zope.conf file zope_conf = %(here)s/etc/zope-debug.conf }}} Отладочная конфигурация использует '''filter-app''' вместо '''app''' для подключения WSGI middleware. На сегодняшний день включено только ('''z3c.evalexception#ajax''') middleware. Вы можете посмотреть документацию !PastDeploy на предмет получения более подробной информации об этих разделах конфигурации. Конфигурационный файл Zope определен здесь (''etc/zope-debug.conf'') по другому, нежели при настройках для внедрения. == Конфигурация Zope == Подобно конфигурации !PasteDeploy, конфигурация Zope состоит из файлов ''etc/zope.conf'' и ''etc/zope-debug.conf''. Вот содержимое ''etc/zope.conf'': {{{#!highlight xml # Identify the component configuration used to define the site: site-definition etc/site.zcml path var/filestorage/Data.fs blob-dir var/blob # Uncomment this if you want to connect to a ZEO server instead: # # server localhost:8100 # storage 1 # # ZEO client cache, in bytes # cache-size 20MB # # Uncomment to have a persistent disk cache # #client zeo1 # # This sets up logging to both a file and to standard output (STDOUT). # The "path" setting can be a relative or absolute filesystem path or # the tokens STDOUT or STDERR. path var/log/z3.log formatter zope.exceptions.log.Formatter path STDOUT formatter zope.exceptions.log.Formatter }}} В файле ''zope.conf'' указывается главный загружаемый ZCML файл (определения сайта). Все пути указываются относительно папки верхнего уровня, где находится файл настроек !PasteDeploy. == Определение сайта == !BlueBream использует ZCML для индивидуальной конфигурации приложения. ZCML - XML-ный декларативный язык настроек. Как вы только что видели в файле ''zope.conf'', главный файл настроек размещается в файле ''etc/site.zcml''. Вот стандартный листинг: {{{#!highlight xml }}} Главный файл настроек ''site.zcml'' содержит ссылки на другие файлы настроек, индивидуальные для каждого пакета. ZCML включает несколько директив, как '''include''', '''page''', '''defaultView''' и т.д., доступные через разные пространства имен XML. В ''site.zcml'' по умолчанию пространство имен XML - http://namespaces.zope.org/zope. Если вы взглянете в начало ''site.zcml'', вы увидите ссылку на XML-ное пространство имен: {{{#!highlight xml }}} Директива '''include''' доступна в пространство именhttp://namespaces.zope.org/zope. Если вы посмотрите другие файлы настроек, вы увидите, что они используют и другие пространства имен, как http://namespaces.zope.org/browser, содрежащие другие директивы, например, '''page'''. В конце ''site.zcml'' включены индивидуальные конфигурационные файлы для проекта. Например, следующая директива: {{{!#highlight xml }}} обеспечит загрузку файла ''src/tc/collector/configure.zcml''. Вы можете определить общую конфигурацию всего приложения в ''site.zcml''. Содержимое файла ''src/tc/collector/configure.zcml'' примерно следующее: {{{#!highlight xml }}} Файл '''securitypolicy.zcml''' содержит определение политик безопасности. Как видно из создержимого файла '''configure.zcml''', он подключает пакет '''welcome'''. По умолчанию, если вы подключаете пакет, и я вно не указываете имя файла настроек, он подключит '''configure.zcml'''. == Метаданные пакета == !BlueBream использует Setuptools для создания дистрибутива приложения, но вы можете легко заменить его на Distribute. Файл setup.py для вашего пакета ''ticketcollector'' будет иметь следующий вид: {{{#!highlight python from setuptools import setup, find_packages setup(name='ticketcollector', version='0.1', description='Ticket Collector', long_description="""\ A ticket collector application""", # Get strings from http://www.python.org/pypi?%3Aaction=list_classifiers classifiers=[], keywords='', author='Baiju M', author_email='baiju@example.com', url='', license='ZPL', package_dir={'': 'src'}, packages=find_packages('src'), namespace_packages=['tc',], include_package_data=True, zip_safe=False, install_requires=['setuptools', 'zope.securitypolicy', 'zope.component', 'zope.annotation', 'zope.app.dependable', 'zope.app.appsetup', 'zope.app.content', 'zope.publisher', 'zope.app.broken', 'zope.app.component', 'zope.app.generations', 'zope.app.error', 'zope.app.interface', 'zope.app.publisher', 'zope.app.security', 'zope.app.form', 'zope.app.i18n', 'zope.app.locales', 'zope.app.zopeappgenerations', 'zope.app.principalannotation', 'zope.app.basicskin', 'zope.app.rotterdam', 'zope.app.folder', 'zope.app.wsgi', 'zope.formlib', 'zope.i18n', 'zope.app.pagetemplate', 'zope.app.schema', 'zope.app.container', 'zope.app.debug', 'z3c.testsetup', 'zope.app.testing', 'zope.testbrowser', 'zope.login', 'zope.app.zcmlfiles', ], entry_points = """ [paste.app_factory] main = tc.main.startup:application_factory [paste.global_paster_command] shell = tc.main.debug:Shell """, ) }}} Большинство данных в файле ''setup.py'' получено при вводе данных во время создания проекта по шаблону. Ключевой аргумент '''install_requires''', который содержит список всех зависимостей пакета. Существует две точки входа. Первая используется в !PasteDeploy для поиска фабрики WSGI приложения, а вторая регистрирует команду скрипта '''paster''' с именем '''shell'''. == Запуск тестов == !BlueBream использует ''zope.testing'' в качестве главного фреймворка для автоматизованного тестирования. Вместе с ''zope.testing'' можно также использовать модули unittest и doctest, которые входят в стандратную библиотеку языка Python. Также есть модуль ''zope.testbrowser'', который используется для функционального тестирования. Для настройки тестов, слоев, и т.д. !BlueBream использует пакет ''z3c.testsetup''. !BlueBream использует рецепт системы Buildout - ''zc.recipe.testrunner'' для генерации скрипта запуска. Если вы посмотрите на конфигурацию сборщика, то увидите там часть, отвечающую за настрйку запуска тестов: {{{!#highlight ini [test] recipe = zc.recipe.testrunner eggs = ticketcollector }}} Рецепт '''testrunner''' создает скрипт запуска тестов на основе модуля ''zope.testing''. Единственная необязательная опция - '''eggs''', в которой приводится список необходимых яиц. Для запуска всех тестов используется команда '''bin/test''': {{{#!highlight bash jack@computer:/projects/ticketcollector$ bin/test }}} Эта команда отыщет все тесты и выполнит их. == Создание объекта приложения == === Контейнеры === В этом разделе мы рассмострим одну из основных концепций !BlueBream - контейнеры. Как упоминалось раньше, !BlueBream использует объектную базу данных ZODB для хранения объектов. Представьте себе ее как контейнер для хранения объектов; внутренний объект этого контейнера может служить контейнером для других объектов, и так далее. Иерархию объектов можна представить следующим образом: {{{ +-----------------------+ | | | +---------+ +--+ | | | | +--+ | | | +--+ | | | | +--+ | | | +---------+ +--+ | | +--+ | +-----------------------+ }}} !BlueBream позаботится о персистентности объектов. Для того, чтобы сделать объект персистентным, его нужно пронаследовать от '''persistent.Persistent'''. Вот некоторые классы в !BlueBream, которые наследуются от '''persistent.Persistent''': * zope.container.btree.BTreeContainer * zope.container.folder.Folder * zope.site.folder.Folder Если вы пронаследуетесь от любого из этих классов, экземпляр важего класса будет персистентным. Вторая вещь, которую вам необходимо сделать, чтобы сделать объект персистентным - добавить его в существующий контейнер. Вы можете проекспериментировать, используя отладочную оболочку !BlueBream. Но перед этим, создайте класс контейнера где-нибудь в коде, и проимпортируйте его. Вы можете добавить это определения контейнера в файл ''src/tc/collector/__init__.py'' (удалите этот код после экспериментов): {{{#!highlight python from zope.container.btree import BTreeContainer class MyContainer(BTreeContainer): pass }}} Теперь откройте отладочную оболочку, как показано ниже: {{{#!highlight bash $ ./bin/paster shell debug.ini ... Welcome to the interactive debug prompt. The 'root' variable contains the ZODB root folder. The 'app' variable contains the Debugger, 'app.publish(path)' simulates a request. >>> }}} Название '''root''' обозначает корень дерева объектов базы данных (контейнер верхнего уровня). Проимпортируйте ваш собственный класс, создайте его экземпляр, и добавльте его в корневую папку: {{{#!highlight python >>> from tc.main import MyContainer >>> root['c1'] = MyContainer() }}} ZODB - транзакционная база данных, так что после внесения изменений, вам придется их зафиксировать (commit). Для того, чтобы зафиксировать (commit) изменения используйте функцию '''transaction.commit''', как показано ниже: {{{#!highlight python >>> import transaction >>> transaction.commit() }}} Теперь, когда вы покинете отладочную оболочку, и откроее ее снова, вы увидите, что можете получить доступ к персистентному объекту: {{{#!highlight bash $ ./bin/paster shell debug.ini ... Welcome to the interactive debug prompt. The 'root' variable contains the ZODB root folder. The 'app' variable contains the Debugger, 'app.publish(path)' simulates a request. >>> root['c1'] }}} Делать такие случайные объекты персистентными - не очень хорошая идея. Следующий раздел объяснит, как создавать формальную схему объектов. Теперь вы можете удалить объект из базы, а определение класса '''MyContainer''' из файла ''src/tc/collector/__init__.py''. Вы можете удалить объект так: {{{#!highlight python >>> del(root['c1']) >>> import transaction >>> transaction.commit() }}} === Объявление интерфейса === {{{#!wiki note Если вы никогда не работали с ''zope.interface'', перед тем как продолжить, рекомендуем вам прочесть раздел '''Interface''' руководства по !BlueBream. }}} В качестве первого шага по созданию главного контейнера приложения, который будет хранить все другие объекты, вам следует создать его интерфейс. Давайте назовем его '''ICollector'''. Для того, чтобы указать интерфейсу описывать контейнер, необходимо пронаследованть его от '''zope.container.interfaces.IContainer''' или любого другого интерфейса, который от него наследуется. Также в главный контейнер приложения рекомендуется добавлять менеджер сайта. Для того, чтобы позже добавить менеджер сайта, рекомендуется пронаследоваться от интерфейса '''zope.site.interfaces.IFolder'''. IFolder наследуется от IContainer. Давайте создадим новый пакет Python с именем '''collector''' в ''src/tc'': {{{#!highlight bash $ mkdir src/tc/collector $ echo "# Python Package" > src/tc/collector/__init__.py }}} Теперь можно создать файл '''src/tc/collector/interfaces.py''', который будет хранить наши интерфейсы: {{{#!highlight python from zope.site.interfaces import IFolder from zope.schema import TextLine from zope.schema import Text class ICollector(IFolder): """The main application container""" name = TextLine( title=u"Name", description=u"Name of application container", default=u"", required=True) description = Text( title=u"Description", description=u"Description of application container", default=u"", required=False) }}} Определенный здесь интерфейс - это схема для главного объекта приложения. В схеме определены два поля: '''name''' и '''description'''. Эта схема позже может быть использована для автоматической генерации веб форм. === Реализация интерфейса === Схема может быть описана как план ваших объектов, так как определяет поля, которые объект должен реализовать, и действия, которые он должен выполнять. После этого, вы можете создавать конкретные классы, которые реализуют вашу схему. Дальше вам нужно релизовать описанный интерфейс. Для реализации IContainer, вы можете пронаследоваться от '''zope.site.folder.Folder'''. Вы можете создать реализацию в ''src/tc/collector/ticketcollector.py'': {{{#!highlight python from zope.interface import implements from zope.site.folder import Folder from tc.collector.interfaces import ICollector class Collector(Folder): """A simple implementation of a collector using B-Tree Container.""" implements(ICollector) name = u"" description = u"" }}} Для того, чтобы объявить, что класс реализует некий интерфейс, вы можете использовать функцию '''implements''' из пакета ''zope.interface''. === Регистрация компонентов === Когда интерфейсы и их реализации готовы, нужно выполнить их настройку в ZCML. Откройте файл ''src/tc/collector/configure.zcml'' в режжиме редактирвоания и введите следующее для регистрации интерфейса ICollector как компонента содержимого: {{{#!highlight xml }}} '''zope.app.content.interfaces.IContentType''' представляет контент-тип. Если интерфейс предоставляет тип интерфейса '''IContentType''', то все объекты, которые предоставляют этот интерфейс считаются контент-объектами. Для установки аннотаций для объектов коллектора нам нужно указать, что он реализует интерфейс '''zope.annotation.interfaces.IAttributeAnnotatable'''. Пример конфигурации ниже также объявляет, что наш класс '''Collector''' реализует интерфейс '''zope.container.interfaces.IContentContainer'''. Эти два класса - примеры интерфейсов-маркеров (marker interfaces), то есть интерфейсов, которые обозначают, что объект принадлежит к специальному типу. Преимущество в том, что для этого не вводятся никакие дополнительные атрибуты или методы. В этом же файле (''src/tc/collector/configure.zcml'') перед директивой '''''' добавьте следующие строки: {{{#!highlight xml }}} Директива '''class''' - это комплексная директива, то есть может содержать поддирективы, такие как '''implements''' и '''require'''. Директива '''class''' также содержит настройки прав доступа к экземплярам класса '''Collector'''. === Вид для добавления коллекторов === Теперь содержимое компонента готово к использованию, но здесь нужна также веб страница, которая позволить добавить объект '''коллектор заявок'''. Для создания формы вы можете использовать пакет ''zope.formlib''. Для этого нужно добавить определение класса в файл ''src/tc/collector/views.py'' следующим образом: {{{#!highlight python from zope.site import LocalSiteManager from zope.formlib import form from tc.collector.interfaces import ICollector from tc.collector.ticketcollector import Collector class AddTicketCollector(form.AddForm): form_fields = form.Fields(ICollector) def createAndAdd(self, data): name = data['name'] description = data.get('description', u'') collector = Collector() collector.name = name collector.description = description self.context[name] = collector collector.setSiteManager(LocalSiteManager(collector)) self.request.response.redirect(".") }}} Функция '''createAndAdd''' будет вызвана, когда пользователь нажмет кнопку '''Add''' в веб форме. Последняя строчка сзади очень важная: {{{#!highlight python collector.setSiteManager(LocalSiteManager(collector)) }}} Эта строчка добавляет менеджер сайта в коллектор с тем, чтобы использовать его как локальный реестр компонентов, таких как, например, локальные утилиты. Как вы уже успели увидеть в предыдущем разделе директива '''browser:page''' используется для регистрации страницы. Вы можете использовать имя '''add_ticket_collector''', чтобы зарегистрировать ее на интерфейс '''zope.site.interfaces.IRootFolder'''. Добавьте эти строчки в файл ''src/tc/collector/configure.zcml'': {{{#!highlight xml }}} Разработка пакета теперь закончена, но он еще не подключен из главного пакета. Для того, чтобы включить этот пакет в главный (''tc.main''), вам следует изменить файл ''src/tc/main/configure.zcml'', добавив эту строчку перед '''''': {{{#!highlight xml }}} Для добавления коллектора заявок сначала необходимо войти в сайт используя логин форму: http://localhost:8080/@@login.html. Вы можете предоставить данные удостоверения, которые присутствуют в файле ''src/tc/main/securitypolicy.zcml'': {{{#!highlight xml }}} По умолчанию имя пользователя и пароль - '''admin''', '''admin'''. Вам следует изменить их на другие. После успешного входа, перейдите по ссылке: http://localhost:8080/@@add_ticket_collector. Вы должны увидеть форму, в кторой можно ввести данные для полей '''name''' и ''' description'''. Введите в поле имя значение '''mycollector'''. После ввода данных, отправьте форму. Вы можете увидеть, что размер файла ''var/filestorage/Data.fs'' растет с добавлением объектов. '''Data.fs''' - файл, в котором физиически хранятся персистентные объекты. Вы также можете кдостоверится, что объект действительно сохранен в базе данных через оболочку Python. Если вы войдете в оболочку Python и попытаетесь получить доступ к корневому объекту, то увидите что в нем есть вами добавленный объект: {{{#!highlight bash jack@computer:/projects/ticketcollector$ ./bin/paster shell debug.ini ... Welcome to the interactive debug prompt. The 'root' variable contains the ZODB root folder. The 'app' variable contains the Debugger, 'app.publish(path)' simulates a request. >>> list(root.keys()) [u'mycollector'] }}} Через отладочную оболочку вы можете просматривать, добавлять, обновлять или удалять Python объекты или атрибуты. === Вид по умолчанию для коллектора === Если вы попытаетесь получить доступ к коллектору по ссылке http://localhost:8080/mycollector, то получите ошибку '''NotFound''', примерно следующего содержания: {{{ URL: http://localhost:8080/mycollector ... NotFound: Object: , name: u'@@index' }}} Эта ошибка возникает, потому что нет вида с именем index, зарегистрированного на интерфейс '''ICollector'''. Этот раздел покажет вам, как создать вид по умолчанию для интерфейсв '''ICollector'''. Как вы уже увидели в разделе ''Первые шаги'', вы можете создать простой вид и зарегистрировать его в ZCML. В файле ''src/tc/collector/views.py'' добавьте новый вид: {{{#!highlight python class TicketCollectorMainView(form.DisplayForm): def __call__(self): return "Hello ticket collector!" }}} Потом добавьте следующее в ''src/tc/collector/configure.zcml'': {{{#!highglight xml }}} Теперь пройдите по ссылке http://localhost:8080/mycollector. Вы должны увидет следующее: {{{ Hello ticket collector! }}} В следующем разделе вы получите информацию о главной странице для коллектора, а также об использовании шаблонов страниц Zope (Zope Page Templates). == Создание главной страницы == === Страница браузера === Страница браузера может быть создана с использованием шаблона. '''form.!DisplayForm''' поддерживает атрибуты '''template''' и '''form_fields'''. Вы также можете убрать метод '''_ _call_ _''' из '''!TicketCollectorMainView'''. Обновите класс '''!TicketCollectorMainView''' в файле ''src/tc/collector/views.py'' до следующего вида: {{{#!highlight python from zope.browserpage import ViewPageTemplateFile class TicketCollectorMainView(form.DisplayForm): form_fields = form.Fields(ICollector) template = ViewPageTemplateFile("collectormain.pt") }}} Вы можете создать ''src/tc/collector/collectormain.pt'' со следующим содержимым: {{{#!highlight html Welcome to ticket collector! Welcome to ticket collector! }}} Теперь перейдите по ссылке http://localhost:8080/mycollector. Вы должны увидеть {{{#!highlight html Welcome to ticket collector!. }}} == Выводы == Эта часть учебника покрывает основы создания веб приложений с помощью !BlueBream. Мы основательно описали, как использовать шаблон проекта bluebream для создания нового проекта. Мы обсудили процесс построения приложения с помощью Buildout. Мы создали контейнер приложения. И наконец, вид по умолчанию для созданного контейнера приложения. Учебник - часть 2 расширяет приложения путем введения дополнительных функций. '''Перевод: Ростислав Дзинько'''