×Закрыть

Пробуем Pylons, часть 1: install, db setup

Всякий человек естественно пытается облегчить себе жизнь. Программисты — ярко выраженное доказательство этого принципа. Так уж сложилось, что по ходу работы мне необходимо создавать множество мелких Web-приложений, для решения небольших текущих задач. Ну а поскольку использование PHP мне не доставляет удовольствия чисто эмоционально, я решил попробовать для этих целей Python.

Для начала был опробован очень неплохой пакет httpy, но его функциональность в области «усиленного облегчения жизни» явно очень аскетична. Услышав краем уха волшебное слово фреймворк (как это лучше всего перевести на русский?) я ринулся на «раскопки» этой информации.

В ходе опробования самых распространенных Python фреймворков я для себя остановился на Pylons.

Хочу сразу пояснить, что TurboGears я пока не рассматривал подробно, так как у них до сих пор нет версии для Python 2.5, а в Django до сих пор по моему дилетантскому мнению обладает слишком большим количеством «магии».

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

Для начала, рассмотрим установку данного фреймворка. Проще всего это сделать используя скрипт easy_install (если он уже установлен в системе).

<pre>$easy_install Pylons</pre>
Для тех, чей доступ в интернет перекрыт злобными прокси-серверами, я бы порекомендовал установить переменную окружения HTTP_PROXY, равную URL прокси-сервера. По крайней мере, именно это решение позволило мне насладиться автоматической установкой Pylons.

Если скрипта easy_install у вас нет, то вам необохдимо скачать peak.telecommunity.com/...ist/ez_setup.py и выполнить его следующим образом.

<pre>$ python ez_setup.py Pylons</pre>

Кроме установки базовых компонент Pylons, можно инсталлировать еще ряд дополнительных. Делается это командой:

<pre>$easy_install Pylons[компонент]</pre>
Где компонент может быть одним из следующих.
— pudge — поддержка сборки документации (подробности тут). Разработчики отмечают что часть инструментов данного проекта все еще находится в разработке и нестабильна.
— genshi, cheetah, kid — поддержка соответсвующих шаблонных движков (подробности тут)

— full — все вышеперечисленное

Обновить Pylons так же очень просто. Независимо от варианта установки, обновление делается одной командой:

<pre>$ easy_install -U Pylons</pre>

Хотя следует отметить, что у меня после обновления таким образом до 0.9.5 Pylons оказался поломан, но разбираться в чем была причина — честно говоря было лень, и переустановка решила все проблемы.

Кстати, для пользователей ОС для домохозяек рекомендуется включить в PATH каталог Scripts, расположенный в каталоге python. Это поможет проще использовать утилиту paster.

Теперь давайте опробуем работу свежеустановленного ферймворка на каком-нибудь конкретном примере.

В роли примера, пожалуй, выступит онлайновый телефонный справочник.

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

<pre>$ paster create --template=pylons phones</pre>

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

Для теста я использую обычно базу SQLite (хозяевам Python 2.5 уже повезло иметь ее в дистрибутиве, остальным придется доставить соответствующий пакет).

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

<pre>sqlalchemy.dburi = sqlite:///%(here)s/phones.db</pre>

Теперь приступим к созданию моделей. Модели можно редактировать прямо в файле __init__.py в каталоге phones/models

Предварительная версия модели выглядит так:

<pre>from sqlalchemy import * from sqlalchemy.ext.assignmapper import assign_mapper from pylons.database import create_engine from pylons.database import session_context as ctx meta = MetaData() persons_table = Table('persons', meta, Column('id', Integer, primary_key=True), Column('name', String(40)), Column('department', Integer, ForeignKey('departments.id')), Column('email', String(40)), Column('phone', String(16)), Column('phone_int', String(6)) ) contacts_table = Table('contacts', meta, Column('id', Integer, primary_key=True), Column('type', Integer), Column('contact', String(40)), Column('person_id', Integer, ForeignKey('persons.id')) ) departments_table = Table('departments', meta, Column('id', Integer, primary_key=True), Column('name', String(40)) ) class Person(object): def __str__(self): return "%s [%s]" % (self.name, self.department) class Contact(object): def __str__(self): return self.contact contact_mapper = assign_mapper(ctx, Contact, contacts_table) person_mapper = assign_mapper(ctx, Person, persons_table, properties={'contacts':relation(Contact)})</pre>
Мы создаем три таблицы:
— persons — основная таблица с контактной информацией
— contacts — дополнительные контактные данные (если понадобится что-то нестандартное)

— departmens — таблица подразделений

Кроме того, создаем обеъкты для ORM, которые связываем с соответствующими таблицами функцией assign_mapper.

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

<pre>import paste.deploy from pylons.database import create_engine import phones.models as model def setup_config(command, filename, section, vars): conf = paste.deploy.appconfig('config:' + filename) conf.update(dict(app_conf=conf.local_conf, global_conf=conf.global_conf)) paste.deploy.CONFIG.push_process_config(conf) uri = conf['sqlalchemy.dburi'] engine = create_engine(uri) print "Connecting to database %s" % uri model.meta.connect(engine) print "Creating tables" model.meta.create_all()</pre>

Теперь, если все сделано правильно, команда

<pre>$ paster setup-app development.ini</pre>

приведет к созданию нашей базы.

Продолжение статьи можно найти тут.

  • Популярное

23 комментария

Подписаться на комментарииОтписаться от комментариев Комментарии могут оставлять только пользователи с подтвержденными аккаунтами.

phones.models нужно исправить на phones.modelу меня другие грабли: Error Tracebackclear thisclear thisModule phones.controllers.phone: 14 in index > c.persons = Person.select () exceptions.TypeError: unbound method select () must be called with Person instance as first argument (got nothing instead)

а... я не следил. видать что-то поменяли: -)

В общем, ошибка “ImportError: No module named models” возникает на pylons-0.9.6rc2-py2.5. на pylons-0.9.5 без проблем.

Зависит от трафика между модулями, и от того как сам подойдешь к архитектуре:) Ошибка была и со вставленным кодомв “c: webPython25phonesphonesmodel< a href= ‘http://init.py’ rel= ‘nofollow’ > init.py”: from sqlalchemy import *from sqlalchemy.ext.assignmapper import assign_mapperfrom pylons.database import create_enginefrom pylons.database import session_context as ctxmeta = MetaData () persons_table = Table (’persons’, meta, Column (’id’, Integer, primary_key=True), Column (’name’, String (40)), Column (’department’, Integer, ForeignKey (’departments.id’)), Column (’email’, String (40)), Column (’phone’, String (16)), Column (’phone_int’, String (6)) ) contacts_table = Table (’contacts’, meta, Column (’id’, Integer, primary_key=True), Column (’type’, Integer), Column (’contact’, String (40)), Column (’person_id’, Integer, ForeignKey (’persons.id’)) ) departments_table = Table (’departments’, meta, Column (’id’, Integer, primary_key=True), Column (’name’, String (40)) ) class Person (object): def str (self): return “%s [%s] ” % (self.name, self.department) class Contact (object): def str (self): return self.contactcontact_mapper = assign_mapper (ctx, Contact, contacts_table) person_mapper = assign_mapper (ctx, Person, persons_table, properties= {’contacts’: relation (Contact)})

в каком *.py должен лежать модуль «phones.models»?

в каталоге phones есть католог models. его можно использовать как пакет. для єтг надо модели описать в нем в файле __init__.pyтогда можно будет импортировать phones.models

Интересное замечание от Paster-а: «DeprecationWarning: pylons.databaseis deprecated, and will be removed from a future version of Pylons. SQLAlchemyusers are recommended to migrate to SAContext» — гибкость модульности в действии

более тесная интеграция это зачасту положительно.

Интересное замечание от Paster-а: “DeprecationWarning: pylons.databaseis deprecated, and will be removed from a future version of Pylons. SQLAlchemyusers are recommended to migrate to SAContext” — гибкость модульности в действии:)

еще одна была заморочка при установке: я думал sqlalchemy идет в комплекте с pylons, поэтому доставлял его потом отдельно командой «python.exe ez_setup.py SQLAlchemy==0.3.10»

все сделал по примерув каком *.py должен лежать модуль «phones.models»?

устанавливаем: Microsoft Windows [Version 5.2.3790] © Copyright 1985−2003 Microsoft Corp.c: webPython25phones> paster setup-app development.inic: webPython25phonesphones< a href= “websetup.py” rel= “nofollow” > websetup.py: 7: DeprecationWarning: pylons.database is deprecated, and will be removed from a future version of Pylons. SQLAlchemyusers are recommended to migrate to SAContext (http://cheeseshop.python.org/p.../SAContext) for similar functionality from pylons.database import create_engineTraceback (most recent call last): File “c: webPython25Scripts< a href= ‘paster-script.py’ rel= ‘nofollow’ > paster-script.py”, line 8, in load_entry_point (’pastescript==1.3.6dev-r6755’, ’console_scripts’, ’paster’) () File “c: webpython25libsite-packagespastescript-1.3.6dev_r6755-py2.5.eggpastescript< a href= ‘command.py’ rel= ‘nofollow’ > command.py”, line 78, in run invoke (command, command_name, options, args [1:]) File “c: webpython25libsite-packagespastescript-1.3.6dev_r6755-py2.5.eggpastescript< a href= ‘command.py’ rel= ‘nofollow’ > command.py”, line 117, in invoke exit_code = runner.run (args) File “c: webpython25libsite-packagespastescript-1.3.6dev_r6755-py2.5.eggpastescript< a href= ‘appinstall.py’ rel= ‘nofollow’ > appinstall.py”, line 68, in run return super (AbstractInstallCommand, self).run (new_args) File “c: webpython25libsite-packagespastescript-1.3.6dev_r6755-py2.5.eggpastescript< a href= ‘command.py’ rel= ‘nofollow’ > command.py”, line 212, in run result = self.command () File “c: webpython25libsite-packagespastescript-1.3.6dev_r6755-py2.5.eggpastescript< a href= ‘appinstall.py’ rel= ‘nofollow’ > appinstall.py”, line 456, in command self, config_file, section, self.sysconfig_install_vars (installer)) File “c: webpython25libsite-packagespastescript-1.3.6dev_r6755-py2.5.eggpastescript< a href= ‘appinstall.py’ rel= ‘nofollow’ > appinstall.py”, line 583, in setup_config mod = import_string.try_import_module (mod_name) File “c: webpython25libsite-packagespaste-1.4-py2.5.eggpasteutilimport_string.py”, line 81, in try_import_module return import_module (module_name) File “c: webpython25libsite-packagespaste-1.4-py2.5.eggpasteutilimport_string.py”, line 67, in import_module mod = import (s) File “c: webPython25phonesphones< a href= ‘websetup.py’ rel= ‘nofollow’ > websetup.py”, line 8, in import phones.models as modelImportError: No module named models

создаем проектc: webPython25> paster create —template=pylons phonesSelected and implied templates: pylons#pylons Pylons application templateVariables: egg: phones package: phones project: phonesCreating template pylonsCreating directory.phones Recursing into +egg+.egg-info Creating.phonesphones.egg-info/ Copying paste_deploy_config.ini_tmpl_tmpl to.phonesphones.egg-infopaste_deploy_config.ini_tmpl Recursing into +package+ Creating.phonesphones/ Copying init.py_tmpl to.phonesphones< a href= “http://init.py” rel= “nofollow” > init.py Recursing into config Creating.phonesphonesconfig/ Copying init.py_tmpl to.phonesphonesconfig< a href= “http://init.py” rel= “nofollow” > init.py Copying environment.py_tmpl to.phonesphonesconfig< a href= “environment.py” rel= “nofollow” > environment.py Copying middleware.py_tmpl to.phonesphonesconfig< a href= “middleware.py” rel= “nofollow” > middleware.py Copying routing.py_tmpl to.phonesphonesconfig< a href= “routing.py” rel= “nofollow” > routing.py Recursing into controllers Creating.phonesphonescontrollers/ Copying init.py_tmpl to.phonesphonescontrollers< a href= “http://init.py” rel= “nofollow” > init.py Copying error.py_tmpl to.phonesphonescontrollers< a href= “error.py” rel= “nofollow” > error.py Copying template.py_tmpl to.phonesphonescontrollers< a href= “template.py” rel= “nofollow” > template.py Recursing into lib Creating.phonesphoneslib/ Copying init.py_tmpl to.phonesphoneslib< a href= “http://init.py” rel= “nofollow” > init.py Copying app_globals.py_tmpl to.phonesphoneslib< a href= “app_globals.py” rel= “nofollow” > app_globals.py Copying base.py_tmpl to.phonesphoneslib< a href= “base.py” rel= “nofollow” > base.py Copying helpers.py_tmpl to.phonesphoneslib< a href= “helpers.py” rel= “nofollow” > helpers.py Recursing into model Creating.phonesphonesmodel/ Copying init.py_tmpl to.phonesphonesmodel< a href= “http://init.py” rel= “nofollow” > init.py Recursing into public Creating.phonesphonespublic/ Copying index.html_tmpl to.phonesphonespublicindex.html Recursing into templates Creating.phonesphonestemplates/ Recursing into tests Creating.phonesphonestests/ Copying init.py_tmpl to.phonesphonestests< a href= “http://init.py” rel= “nofollow” > init.py Recursing into functional Creating.phonesphonestestsfunctional/ Copying init.py_tmpl to.phonesphonestestsfunctional< a href= “http://init.py” rel= “nofollow” > init.py Copying test_models.py_tmpl to.phonesphonestests< a href= “test_models.py” rel= “nofollow” > test_models.py Copying websetup.py_tmpl to.phonesphones< a href= “websetup.py” rel= “nofollow” > websetup.py Copying MANIFEST.in_tmpl to.phones< a href= “MANIFEST.in” rel= “nofollow” > MANIFEST.in Copying README.txt_tmpl to.phonesREADME.txt Copying development.ini_tmpl to.phonesdevelopment.ini Recursing into docs Creating.phonesdocs/ Copying index.txt_tmpl to.phonesdocsindex.txt Recursing into ez_setup Creating.phonesez_setup/ Copying README.txt to.phonesez_setupREADME.txt Copying init.py to.phonesez_setup< a href= “http://init.py” rel= “nofollow” > init.py Copying setup.cfg_tmpl to.phonessetup.cfg Copying setup.py_tmpl to.phones< a href= “setup.py” rel= “nofollow” > setup.py Copying test.ini_tmpl to.phonestest.iniRunning c: webPython25python.exe setup.py egg_infoAdding Pylons to paster_plugins.txtAdding WebHelpers to paster_plugins.txt

Вроди все по примеру сделал, но при «paster setup-app» вылазит: "import phones.models as model ImportError: No module named models«В каком «py» они должны лежать, я так понимаю что этот модуль автоматом созадется paster-ом?

Пилоны при инсталляции не смогли поставить Nose нужной версии. Видать что-то натуплено с описанием зависимости: -) Но без nose ЕМНИП все должно фпалне работать.

Что-то сначала easy_install поругался: error: Could not find suitable distribution for Requirement.parse (’nose> =0.9.2,

Откуда у Contact возьмется атрибут phone? В схеме phone принадлежит Person.

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

Откуда у Contact возьмется атрибут phone? В схеме phone принадлежит Person.

Про «чёрную магию» Django — ничего подобного. Не будем плодить мифы?

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

Про «чёрную магию» Django — ничего подобного. Не будем плодить мифы?

Отличная работа!

pythy говорит: 27.04.2007 в 19: 34 Исходник поправь, код съехал.Статья понравилась. Жде продолжения.

спасибо: -)

CB говорит: 27.04.2007 в 20: 06 Використовуйте тег для блоків коду

я использовал тег

но как его недостаточно. сейчас исправлю.

Використовуйте тег < pre> для блоків коду

Исходник поправь, код съехал.Статья понравилась. Жде продолжения.

Подписаться на комментарии