FastNetMon

Показаны сообщения с ярлыком Pylons. Показать все сообщения
Показаны сообщения с ярлыком Pylons. Показать все сообщения

пятница, 16 декабря 2011 г.

четверг, 13 мая 2010 г.

Разработка веб-приложений на базе Pylons, Python

Этот пост представляет собой объединение ряда моих постов под общей темой в единый материал по Pylons.

1. Почему Python круче Perl в веб-разработке. Статья
2. Почему Pylons/Python круче Rails/Ruby и Catalyst/Perl? Статья
3. Установка Pylons 0.9.7 на Debian 5 Lenny. Статья
4. Использование Pylons в продакшене: paster + Nginx Статья
5. Создание первого контроллера. Статья
6. Работа с шаблонами. Статья
7. Передача переменных из контроллера в шаблон. Статья
8. Обработка GET/POST параметров. Статья
9. Работа с сессиями. Статья
10. Подключение базы данных (PostgreSQL, MySQL). Статья
11. Работа с БД посредством SQLAlchemy. Статья
12. Работа с моделью из внешних скриптов. Статья
13. Подключение ToscaWidgets к Pylons для работы с формами. Статья
14. Валидация форм посредством ToscaWidgets. Статья
15. Аутентификация посредством AuthKit. Статья
16. Авторизация посредством AuthKit. Статья

Есть также идея написать еще посту про пагинацию, кэширование и REST интерфейсы. Если Вам интересно что-то конкретное, то милости прошу в комменты :)

пятница, 16 апреля 2010 г.

Использование Pylons в продакшене: paster + Nginx

Разработку приложения можно вполне вести и на встроенном веб-сервере paster, но как только посещаемость увеличивается и Вы запускаете проект, то, безусловно, paster - не лучший выбор. Тут ем может помочь Nginx, выступающий в качестве reverse proxy!

Итак, имеем paster на 5000 порту:
http://127.0.0.1:5000


Ставим Nginx (здесь и далее - ОС Debian 5 Lenny):
apt-get install -y nginx


Удаляем конфиг стандартного сайта:
rm /etc/nginx/sites-enabled/default


Создаем новый:
vi /etc/nginx/sites-enabled/default


Со следующим содержимым:

server {
listen 80;
server_name domain.ru;

access_log /var/log/nginx/access.log;

location / {
# передаем хостнейм Paster, хотя это не обязательно
proxy_set_header Host $host;
proxy_pass http://127.0.0.1:5000;
}
}


Перезапускаем Nginx и применяем настройки:
/etc/init.d/nginx restart


Следом запускаем paster (иначе поулчим на 80м порту "502 bad gateway"):

cd myapp
nohup paster serve --reload development.ini &


Все, теперь открываем сайт http://domain.ru и любуемся видом нашего Pylons проекта.

воскресенье, 14 марта 2010 г.

Pylons: авторизация посредством AuthKit

Задачу с проверкой логина и пароля мы решили в прошлый раз, теперь хотелось бы в зависимости от логина пользователя давать или не давать ему доступ к определенным контроллерам. Действовать мы будем по мануалу: http://pylonsbook.com/en/1.1/authentication-and-authorization.html#the-authorization-decorator

Теперь открываем наш контроллер:
vi myapp/controllers/main.py


И в блок import ов добавляем следующее:

from authkit.authorize.pylons_adaptors import authorize
from authkit.permissions import RemoteUser, ValidAuthKitUser, UserIn


Теперь мы можем переписать контроллер private в более простом виде :
@authorize(RemoteUser())
def private(self):
return "You are authenticated!"


То есть в декораторе идет проверка является ли юзер залогиненым (установлено ли REMOTE_USER) и только после этого выполняет код метода. При этом в книге по Pylons крайне рекомендуется использовать ValidAuthKitUser() вместо RemoteUser(), так как второй просто проверяет не пустоту поля REMOTE_USER, но не убеждается, что это явно разрешенный в конфиге юзер. Как понимаете, ValidAuthKitUser более безопасен.

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

@authorize(UserIn(['admin2']))
def private2(self):
return "You are admin2!"


И если же попытаться войти на эту страницу от имени admin, то в ответ мы получим "403 Forbidden".

Кроме подключения авторизации через декораторы возможно ее использование как Middleware, подробно это описано вот здесь: http://pylonsbook.com/en/1.1/authentication-and-authorization.html#the-authorization-middleware

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

@authorize(ValidAuthKitUser())
def __before__(self):
pass


Ну вот, пожалуй, и хватит с авторизацией. Но замечу, что кроме описанных фич у AuthKit есть еще очень много всего вкусного, например, группы пользователей / роли (то есть можно гранулировано выдавать полномочия на те или иные подсистемы разным юзерам) / разрешения / поддержка OpenID а также искаробочная возможность привязывать юзеров только к определенным IP.

Pylons: аутентификация посредством AuthKit

Есть вот такая отличная библиотека для всего, что только можно вытворить с авторизацией / аутентификацией в веб-приложении (поддерживаются HTTP basic, HTTP digest, form, cookie OpenID ит.д.) http://authkit.org/. Также на офсайте AuthKit явно указано, что с Pylons он работает ну просто супер :) Вот ее мы и будем подключать, хотя есть еще аналоги в лице http://what.repoze.org/docs/1.0/ и http://static.repoze.org/whodocs/ При этом руководствоваться мы будем вот этой инструкцией: http://pylonsbook.com/en/1.1/authentication-and-authorization.html Также в случае проблем с пониманием "куда вставить этот код" использовалась вот эта сборочка: http://pypi.python.org/pypi/SimpleSite/0.3.0

Настроим аутентификацию (просто проверка логина/пароля без контроля прав доступа к различным ресурсам).

Устанавливаем:
easy_install authkit


Открываем конфигурацию middleware:
vi myapp/config/middleware.py


Добавляем в самый верх, в блок import:
import authkit.authenticate


Также ПЕРЕД блоком:

# Display error documents for 401, 403, 404 status codes (and
# 500 when debug is disabled)
if asbool(config['debug']):
app = StatusCodeRedirect(app)
else:
app = StatusCodeRedirect(app, [400, 401, 403, 404, 500])



С аналогичным отступом добавляем подключение auth middleware:

app = authkit.authenticate.middleware(app, app_conf)


Теперь нам необходимо сконфигурировать AuthKit:
vi development.ini


И в самый низ блока "[app:main]" добавляем следующее:

authkit.setup.method = form, cookie
authkit.form.authenticate.user.data = admin:qwerty
[без выравнивания по = раб. не будут ]admin2:qwerty2
authkit.cookie.secret = secret string
authkit.cookie.signoutpath = /main/signout


Таким образом мы создали одного пользователя и выбрали тип аутентификации - формы/куки, а также выбрали метод, при вызове которого будет происходить logout.

Теперь открываем наш контроллер:

vi myapp/controllers/main.py


После этого создаем там методы:

def private(self):
if request.environ.get("REMOTE_USER"):
return "You are authenticated as %s!" % request.environ.get("REMOTE_USER")
else:
response.status = "401 Not authenticated"
return "You are not authenticated"
def signout(self):
return "Successfully signed out!"


И пробуем открыть его через сеть: http://xx.xx.xx.xx:5000/main/private и там нам будет выдана форма, введя в которую admin/qwerty мы станем аутентифицированным пользователем. Проверка статус аутентификации проверяется по логину, который в случае успешной аутентификации помещается в request.environ.get("REMOTE_USER"). Чтобы "вылогиниться" нам необходимо просто посетить адрес http://xx.xx.xx.xx:5000/main/signout при этом куки файл, хранящий данные о залогинености будет удален.

Все, с аутентификацией закончено :)

Pylons: валидация форм посредством ToscaWidgets

В первой части статьи мы научились просто отображать формы: http://phpsuxx.blogspot.com/2010/03/pylons-toscawidgets.html Теперь мы добавим к этому функцию валидации входных данных от форм. На начальном этапе у Вас должен быть проект со всеми правками с прошлого примера. Руководствоваться мы будем вот этим: http://toscawidgets.org/documentation/tw.forms/tutorials/validate_pylons.html

Открываем код нашего контроллера:
vi myapp/controllers/main.py


Добавляем в самый верх подключение модуля валидации:

from tw.mods.pylonshf import validate


Чуть ниже него добавляем (устанавливаем дополнительный валидатор, чтобы год точно был целым числом):

test_form = twf.TableForm('test_form', action='/main/tosca_save', children=[
twf.HiddenField('id'),
twf.TextField('title'),
twf.TextField('year', size=4, validator=twf.validators.Int),
twf.CalendarDatePicker('release_date'),
twf.SingleSelectField('genera', options=['', 'Action', 'Comedy', 'Other']),
twf.TextArea('description'),
])


Также удаляем старый метод tosca и вместо него добавляем следующее:

def tosca(self, **kw):
return test_form()

@validate(form=test_form, error_handler='index')
def tosca_save(self, **kw):
return str(kw)


То есть, если данные валидны (в поле year целое число), они будут переданы обработчику tosca_save и выданы на экран. Иначе будет сделан "редирет" (в кавычках потому что будет просто выдано его содержимое ) на index контроллер. Ну вот теперь у нас есть крайне удобный способ обработки форм без тупой рутины :)

Pylons: подключение ToscaWidgets к Pylons для работы с формами

Есть вот такая обалденная штука: http://toscawidgets.org/ К ней прилагается куча готовых виджетов, их можете найти здесь: http://pypi.python.org/pypi?:action=search&term=tw&submit=search

Ставим сам ToscaWidgets:

easy_install ToscaWidgets


Ставим виджет работы с формами (к слову, есть еще tw.dynforms, но о нем, думаю, позже):

easy_install tw.forms


Далее двигаемся по инструкции по интеграции ToscaWidgets с Pylons: http://toscawidgets.org/documentation/ToscaWidgets/install/pylons_app.html

Открываем настройки middleware:
vi myapp/config/middleware.py


Добавляем в самый верх:
import tw.api as twa


Далее после строки "CUSTOM MIDDLEWARE HERE" добавляем следующее:

app = twa.make_middleware(app, {
'toscawidgets.framework': 'pylons',
'toscawidgets.framework.default_view': 'mako',
})



Теперь открываем контроллер:
vu myapp/controllers/main.py


И в самый верх добавляем подключение виджета:

import tw.forms as twf


И внизу добавляем еще тестовый метод:

def tosca(self):
test_form = twf.TableForm('test_form', action='/main/tosca_save', children=[
twf.HiddenField('id'),
twf.TextField('title'),
twf.TextField('year', size=4),
twf.CalendarDatePicker('release_date'),
twf.SingleSelectField('genera', options=['', 'Action', 'Comedy', 'Other']),
twf.TextArea('description'),
])

return test_form()


И теперь вызываем со сети наш контроллер: http://xx.xx.xx.xx:5000/main/tosca и любуемся довольно миловидными формочками :)

среда, 21 октября 2009 г.

Pylons: CRUD для SQLAlchemy

Итак, мы дошли до самого интересного -- качественного CRUD интерфейса для работы с SQLAlchemy.

Вот ссылка: http://docs.formalchemy.org/current/ext/pylons.html#administration-interface

Запускаем:
paster create -t pylons_fa myapp


И конфигурируем следующим образом:

Enter admin_controller (Add formalchemy's admin controller) [False]: True
Enter template_engine (mako/genshi/jinja2/etc: Template language) ['mako']: mako
Enter sqlalchemy (True/False: Include SQLAlchemy 0.5 configuration) [False]: True



После этого конфигурируем модель: http://phpsuxx.blogspot.com/2009/10/pylons_18.html

Теперь открываем: http://127.0.0.1/admin

Но у меня закрался баг -- слетели стили и все стало жутко некрасиво (ошибка в стиле: "/admin/_static//admin.css" ).

Переходим в папку cd myapp/public/ и стаскиваем следующие файлы:

wget http://formalchemy.googlecode.com/hg/formalchemy/ext/pylons/resources/edit.png
wget http://formalchemy.googlecode.com/hg/formalchemy/ext/pylons/resources/add.png
wget http://formalchemy.googlecode.com/hg/formalchemy/ext/pylons/resources/admin.css
wget http://formalchemy.googlecode.com/hg/formalchemy/ext/pylons/resources/delete.png


Теперь открываем файл admin.css и заменяем все упоминания png файлов с ./edit.png на edit.png

А также добавляем строки:

.ui-icon-circle-plus {
background-image: url(add.png);
)


Теперь идем в папку: myapp/templates/forms/ и открываем файл: restfieldset.mako и там строчку:

href="${url('fa_static', path_i nfo='/admin.css')}"

меняем на

href="/admin.css"



Итого - вещь ОЧЕНЬ сырая и насмерть завязанная на Mako. Так что ищем альтернативы или пишем сами.

Pylons: работа с формами, FormAlchemy

Обнаружил замечательный инструмент работы с формами: FormAlchemy, он способен генерировать HTML формы из схемы бд SQLAlchemy

Устанавливаем:
easy_install FormAlchemy


Открываем файл:
vim myapp/controllers/main.py
и добавляем в верху подключение модуля:
from formalchemy import FieldSet, Field


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

def form_new(self):
fs = FieldSet(model.Person)
return fs.render()



Для редактирования полей существующего объекта используется следующий контроллер:

def form_edit(self):
my_person = meta.Session.query(model.Person).first()
fs = FieldSet(my_person)
return fs.render()



Также, разумеется, есть возможность изменять данные формы, верифицировать их и вносить измененный объект в базу, эти примеры можете посмотреть на офсайте и на странице проекта.

Тут следует отметить, что FA генерирует только набор полей, но не обрамляет все это в таги form и не добавляет управляющих элементов (submit, cancel).

Итого: штука крайне стремная и крайне сырая и мне НЕ НРАВИТСЯ. Продолжаем поиски дальше :)

Python/Pylons виджет-движок для веб?

Мммм очень интересная штука, концепцию которой я пока не особенно понял:
http://toscawidgets.org/ но подход выглядит очень суперски!

Pylons CRUD, как ?

Сейчас наткнулся на вот такое: http://pylonshq.com/project/pylonshq/wiki/LaurentsNotes Комрады, а какие есть готовые CRUD для Pylons/SQLAlchemy?

Вот еще интересная разработка: http://code.google.com/p/pails-admin/

понедельник, 19 октября 2009 г.

Pylons: работа с моделью из внешних скриптов

Довольно часто приходится обращаться к данным БД не из MVC фреймворка, а из каких-либо связанных скриптов. Как это делать в случае SQLAlchemy мы разберемся сейчас =)

Переходим в папку проекта (myapp) и создаем файл mydbscript.py следующего содержания:


#!/usr/bin/python2.5
# -*- coding: utf-8 -*-

import sqlalchemy as sa
from myapp import model
import myapp.model.meta as meta

DB_URL = "mysql://my_db_user:qwerty@127.0.0.1/my_test_database"
#DB_URL = "postgres://my_db_user:qwerty@127.0.0.1:5432/my_test_database"

engine = sa.create_engine(DB_URL)
model.init_model(engine)

# а далее следует обычный код работы с БД

person_q = meta.Session.query(model.Person)
all_users = person_q.filter(model.Person.name=='Pavel').all()

for user in all_users:
print "User: ", user.name


Делаем скрипт исполняемым и запускаем:

chmod +x mydbscript.py
./mydbscript.py


Вот так довольно легко можно получить все прелести ORM из своего скрипта :)

Pylons: работа с БД средствами SQLAlchemy

Для начала подключаем необходимые для работы классы, в файле
vi myapp/controllers/main.py
в верху добавляем:


from myapp.model import meta
from myapp import model


Первое импортирует служебный класс meta, используемый для работы SQLAlchemy, а второе импортирует наши классы модели.

Теперь в файл
vi myapp/controllers/main.py
добавляем код записи в БД:

def db_write(self):
new_user = model.Person()
new_user.name = "Pavel"

meta.Session.add(new_user)
meta.Session.commit()



Теперь открываем нашу страницу через веб и следим, чтобы все прошло без ошибок:
http://127.0.0.1/main/db_write


Теперь напишем контроллер, который выдаст нам содержимое БД:


def db_read(self):
person_q = meta.Session.query(model.Person)
c.all_users = person_q.filter(model.Person.name=='Pavel').all()

return render("main/db_read.html")


Теперь необходимо создать шаблон:
vim myapp/templates/main/db_read.html


Вот его текст:


<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:py="http://genshi.edgewall.org/"
lang="en">
<ul>
<li py:for="item in c.all_users">${item.id}:${item.name}</li>
</ul>
</html>




Теперь открываем страницу: http://127.0.0.1/main/db_read и любуемся нашим креативом =)

Итак, нам осталось разобраться с формами, пагинацией и все, можно будет приступать к созданию новых клевых сервисов =)

Pylons: обработка GET/POST параметров

Итак, продолжим наполнять жизнью наш веб аппликейшен. Сейчас будем обрабатывать GET и POST параметры, передаваемые нашим контроллерам (для этого используется объект responce.params причем, GET и POST ключи собираются в один словарь для удобства). Это довольно легко, добавляем в файл

vi myapp/controllers/main.py

ещё один метод:


def show_params(self):
return request.params['my_param']


И теперь открываем http://127.0.0.1/main/show_params?my_param=value555 и видим, что параметр передался в тело веб-страницы :)

Pylons: работа с сессиями

Итак, продолжим мучать Pylons :) Сейчас нам для полного веб-счастья не хватает возможности временно сохранять данные между запросами пользователей, для этой цели мы будем использовать сессии (в Pylons они предоставяются компонентом Beaker).

Для тестов добавим в код файла
vi myapp/controllers/main.py
новый метод, count, который будет считать число открытий страницы для данного юзера:

def count(self):
if session.has_key('users_count'):
session['users_count'] = session['users_count'] + 1
else:
session['users_count'] = 0

session.save()

return "Users count: ", session['users_count']


Тут все довольно тривиально, за исключением строки "session.save()", в Pylons необходимо явно указывать, что изменения в сессии требуется сохранить, для этого и вызывается метод save().

Ну и еще - краткое описание внутренностей работы сессий. В cookie пользователю помещается переменная с именем веб-приложения (myapp) со значением "e18f7176b0832b030a5df1041b6ffb877d5377e11e93e0e7e783584523192c65e946fafd", что представляет собой хеш код, по которому производится идентификация владельца сессии. Время жизни сессии - до закрытия браузера.

Ну вот и все, теперь при каждом обновлении страницы http://127.0.0.1/main/count число будет увеличиваться.

Pylons: передача переменных из контроллера в шаблон

После того, как мы освоились с шаблонами пришло самое время научиться передавать переменные в шаблон, вещь ради которой все и затевалось. Открываем контроллер
vi myapp/controllers/main.py


и перед return добавляем строку
c.welcome_text = "Hello World from Variable!"

Теперь надо в шаблоне
vi myapp/templates/main/index.html


отобразить содержимое переменной: удаляем строку "Hello World from Template!" и вносим в него следующий код между тегов html: ${c.welcome_text}

Теперь перезапускаем приложение и любуемся переданным в шаблон текстом :)

Pylons: работа с шаблонами

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

Создаем папку для шаблонов данного контроллера:

mkdir myapp/templates/main


Создаем сам шаблон:
vim myapp/templates/main/index.html


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


<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://genshi.edgewall.org/" lang="en">
Hello World from Template!
</html>


Теперь открываем контроллер:

vi myapp/controllers/main.py

и заменяем
return 'Hello World'
на:
return render("main/index.html")

Теперь запускаем:
paster serve development.ini
и наблюдаем, что шаблон считался из файла (текст "Hello World from Template!" вместо "Hello World!"), а не из кода контроллера, что позволяет разделить разработку логики от разработки визуальной части.

Подробная документация по синтаксису шаблонов: http://genshi.edgewall.org/wiki/Documentation/0.5.x/xml-templates.html

воскресенье, 18 октября 2009 г.

Pylons: создание первого контроллера

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

Создаем свой контроллер:
paster controller main


В результате этого действа будет создан файл: myapp/controllers/main.py, в котором будет один единственный класс (MainController) с одним методом, который покажет нам надпись "Hello world". Также будет создан файл myapp/tests/functional/test_main.py с тестами для данного контроллера, но они нам сейчас не нужны.

Перезапускаем приложение:
paster serve --reload development.ini
и открываем адрес: http://127.0.0.1/main/index

Теперь нам надо, чтобы эта страничка показывалась по адресу: http://127.0.0.1 Для этого открываем файл раутов (где производится настройка того, какой путь какой контроллер обрабатывает):

vi myapp/config/routing.py


И в после строки "# CUSTOM ROUTES HERE" добавляем:
map.connect('/', controller='main', action='index')
и сносим файл шаблона (иначе не будет рабтать новый раут):
rm myapp/public/index.html


Все, стандартный контроллер подключен как индексная страница и по адресу http://127.0.0.1 отображается веселое "Hello world!" :)

Литература по Pylons

Вот суперская книга в открытом доступе: http://pylonsbook.com/
Перед полным погружением рекомендую прочесть: http://wiki.pylonshq.com/display/pylonsfaq/Home
Кукбук: http://wiki.pylonshq.com/display/pylonscookbook/Home
Очень хороший мануал: http://developer.co.ua/posts/view/pylons_python-frejmvork