Пробуем Pylons, часть 2: views
Современный web-development (и не только) просто изобилует всякими модными сокращениями, от множества всяких CRUD, ReST, SOAP, KISS, BRTG, TiMO и так далее. Не последнее место среди всей этой мешанины занимает аббревиатура MVC.
Сейчас даже начинающий программист (если он себя уважает), знает что объединять код и представление — очень плохо. Ну а язык, поощряющий такой подход «by design» — это, соответственно, плохой язык.
Поскольку Python — язык хороший, а Pylons — замечательный Фреймворк, написанный на хорошем языке, то позволить себе такой ужас как генерацию HTML из кода скрипта, он себе явно позволить не может. Это во-первых не модно, во-вторых и неудобно... Представьте себе ужас дизайнера, когда он увидит свой HTML код внутри каких-то странных скриптов, или отвращение программиста, вынужденного загонять многие килобайты кода странички в свой исходник. Вот для исправления этой ситуации и были разработаны так называемые «шаблонные движки» (template engines).
По своей сути, шаблонный движок (или шаблонизатор, тут как всегда трудности перевода), просто-напросто берет текстовый файл, называемый шаблоном, и проводит с ним некие операции. Самая простая операция, например, подстановка значения некой переменной, переданной «снаружи» вызываемым скриптом. Более сложные операции включают в себя проверки условий, выполнения циклов, наследование шаблонов и т.п.
На Python подобных шаблонизаторов написано уже весьма и весьма много. К радости следует отметить, что Pylons поддерживает многие из них сразу, а большинство остальных — через механизм buffet.
Рассмотрению достоинств и недостатков отдельных шаблонных движков можно посвятить целую статью (если будут желающие, я могу за нее взяться), но для Pylons проектов по-умолчанию выбраны шаблоны от Mygthy.
Для начала экспериментов, нам необходимо создать контроллер, который будет управлять нашим приложением-телефонной книгой. Для этого выполним следующую команду:
$paster controller phoneЭта команда, во-первых создаст в каталоге controllers файл phone.py, в котором мы будем размещать текст управляющего контроллера, во-вторых создаст заготовку для функционального теста в каталоге tests/functional. На тестировании я пока тоже фокусироваться не буду, это — тема для отдельной статьи.
Следующее что надо сделать — это подключить наш контроллер и задействовать его в роутинге для сайта.
Для этого необходимо подправить файл config/routing.py, добавив туда одну строчку, чтобы файл выглядел следующим образом:
import os
from routes import Mapper
def make_map(global_conf={}, app_conf={}):
root_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
map = Mapper(directory=os.path.join(root_path, 'controllers'))
map.connect('error/:action/:id', controller='error')
# добавляем новый контроллер
map.connect(':controller/:action/:id', controller='phone', action='index', id=0)
map.connect('*url', controller='template', action='view')
return mapДля простоты отображения данных, я немного до-обработал модель данных для нашей задачи. Теперь она выглядит так:
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
class Department(object):
def __str__(self):
return self.name
contact_mapper = assign_mapper(ctx, Contact, contacts_table)
department_mapper = assign_mapper(ctx, Department, departments_table)
person_mapper = assign_mapper(ctx, Person, persons_table,
properties={'contacts':relation(Contact),
'dep':relation(Department)})Как видно, я добавил отображение для таблицы подразделений и соответствующую связь в таблице персон (хочу заметить что это еще далеко не последнее изменение).
Теперь можно с чистой совестью заняться кодированием отображения данных.
Для этого напишем текст нашего первого контроллера в файле controllers/phone.py
Он очень прост:
from phones.lib.base import *
from phones.models import Person
class PhoneController(BaseController):
def index(self, id):
# выбираем данные
c.persons = Person.select()
# возвращаем отрендереный шаблон
return render_response('list.myt')Для начала, он просто выбирает всех персон, занесенных в телефонную книгу и возвращает ответ путем рендеринга шаблона list.myt.
Осталось только этот самый шаблон создать. Он также в принципе несложен. Создайте в каталоге templates вашего проекта файл list.myt следующего содержания:
<h1 class="main">Title List</h1> <ul id="titles"> % for person in c.persons: <li> <% person.name %> <% person.dep %> % for contact in person.contacts: <% contact %> % #end for </li> % #end for </ul>Он просто выводит в виде списка имена людей и подразделения в которых они работают, а при наличии дополнительных контактов выводит и их.
В нем не хватает очень многого, например, проверки наличия дополнительных контактов, нормального форматирования, пристойного внешнего вида и т.д., но ведь не все так сразу. Я думаю многие хотят уже скорее увидеть все это в действии. Нет ничего проще:
$paster serve --reload development.ini
После этого, по адресу 127.0.0.1:5000/phone мы можем любоваться нашим приложением во всей его красе.

8 коментарів
Підписатись на коментаріВідписатись від коментарів Коментарі можуть залишати тільки користувачі з підтвердженими акаунтами.