пятница, 4 марта 2011 г.

Flask Tips - пред и пост обработка запроса на основе аргументов URL Rule

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

Объект Request в Flask, позволяет получить доступ к аргументам, полученным после парсинга URL. К примеру, у нас имеется Rule('/user/<username>'). При получении запроса, совпадающего с этим правилом, в request будет создан не пустой словарь request.view_args, который будет содержать аргументы для view. Но иногда, есть задачи, когда на основе этих аргументов требуется выполнять какую-то одну и ту же обработку в нескольких view, либо требуется, чтобы такая обработка была выполнена перед выполнением самого view, или возможно постобработка.

В Flask, приложение имеет возможность задать пост и пред обработчики, как уровня приложения, так и уровня модуля (если модули используются в приложении), и при последнем варианте, модуль может объявлять свои обработчики уровня приложения. Все они выполняются после создания объекта _RequestContext, объекты которого будут доступны, при запросе из в обработчиках. Это request, session, g и остальные. Поэтому, в обработчиках можно задействовать и request.view_args.

Рассмотрим на примере. Пример взят из моего дипломного проекта.

Допустим, мы пишем web-приложение, которое работает с несколькими базами данных. Выбор базы зависит от аргументов переданных view. То есть, модули приложения, содержат алгоритмы для обработки данных, и не должны использовать любую базу данных. В нашем примере, выбор базы данных будет основываться на базе полученного имени проекта (база данных на проект). В нашем примере, мы используем MongoDB, и пул соединений (объект mongokit.Connection) доступен как глобальный объект mongodb. Префик URL для всех модулей '/<project>', что позволяет однозначно идентифицировать проект, с которым ведется работа.

Напишем, глобальный обработчик приложения, который помещает в g.projectdb соединение с базой данных текущего проекта.

@app.before_request
def select_project_db():
  if 'project' in request.view_args:
    g.projectdb = mongodb[request.view_args['project']]

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

cursor = g.projectdb.Tasks.find({...})

В вышеприведенном примере, предположим, что Tasks - это зарегистрированная в пуле соединений модель, с указанным __collection__. Просто? Как дважды два. Мы не думаем, какая база, какой проект, когда регистрировать модель (если позаботились о регистрации ранее), не мучаемся импортами. Мы просто используем соединение, и обрабатываем данные, мы думаем о задаче. А если уж потребуется знать, какой проект текущий - то переменная project всегда доступна, как аргумент view.

При этом мы легко можем организовать структуру как отдельно взятой базы, так и структуру баз в целом. Неявный routing получается. А все благодаря, маленькой возможности Flask. =)))