Пользователь
0,0
рейтинг
30 июля 2012 в 23:49

Разработка → Flask-Admin

Доброе время суток.

Хочу представить проект, над которым работал в последнее время: Flask-Admin. Если в двух словах, это расширение для фреймворка Flask, которое позволяет быстро создавать административный интерфейс в стиле Django.

Архитектура


Попробую описать как это все работает и в чем отличие от админки Django.

Базовый кирпичик Flask-Admin это класс у которого есть view методы. Есть немного базового кода, который собирает из кирпичиков админку и рисует меню. Все.

Как такой подход позволяет эффективно строить административный интерфейс?

Возьмем, к примеру, типовую задачу — CRUD для моделей.

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

А можно сделать класс, который умеет работать с одной моделью. Создав экземпляр такого класса и подключив его в админку, получим интерфейс управления этой моделью. Повторяем для остальных моделей и админка почти готова.

Кроме того, при таком подходе можно легко расширять функционал. Вместо monkey patching'а, достаточно переопределить нужные методы и, в результате, получаем новое поведение.

Это и есть основное отличие от Django — возможность быстро и безболезненно расширить или заменить функционал админки под конкретные задачи.

Flask-Admin


Что умеет Flask-Admin, из коробки:
1. Генерацию меню (до двух уровней) из подключенных кирпичиков с учетом правил доступа
2. Возможность управления доступом, без каких либо предположений о используемой системе авторизации
3. Набор базовых классов для создания своих «кирпичиков»
3. CRUD для моделей SQLAlchemy, включая пейджинг, сортировку, фильтры, поиск и тому подобное.
4. Файловый менеджер
5. Локализация. Работает с помощью модифицированной версии Flask-Babel, патч отправлен Армину, но до сих пор не принят. Временно можно установить версию из моего репозитория, она обратно-совместима с текущим stable из PyPI.

Клиентская часть работает поверх Twitter Bootstrap. Причина очень простая — адекватный внешний вид и куча UI вкусностей для быстрого создания UI. Так или иначе, админка обычно недоступна обычным пользователей, а писать UI с bootstrap все же удобнее, чем без него.

Вот так выглядит список моделей для этого примера:


А вот так — встроенный файловый менеджер:


Ближе к коду


И так, для того что бы подключить админку к приложению, нужно:
1. Создать экземпляр класса Admin
2. Добавить экземпляров класса BaseView (все «кирпичики» наследуются от него)

Например, есть две модели: User и Post, нужно создать админку. Код инициализации будет выглядеть так:
from flask.ext.admin import Admin
from flask.ext.admin.contrib.sqlamodel import ModelView

admin = Admin(app)
admin.add_view(ModelView(User, db.session))
admin.add_view(ModelView(Post, db.session))

db.session это сессия алхимии.

ModelView расширяется по образу и подобию Django — есть набор свойств на уровне класса/экземпляра, которые можно менять.

Например, если в списке моделей User нужно исключить поле password, делаем так:
class MyUserAdmin(ModelView):
  excluded_list_columns = ('password',)

admin = Admin(app)
admin.add_view(MyUserAdmin(User, db.session))


Ничто не мешает менять свойства в конструкторе до вызова предка:
class MyUserAdmin(ModelView):
  def __init__(self, session, name, excluded=None):
    if excluded:
      self.excluded_list_columns = excluded
   
    super(MyUserAdmin, self).__init__(User, session, name=name)

admin = Admin(app)
admin.add_view(MyUserAdmin(db.session, 'View1', ('password',))
admin.add_view(MyUserAdmin(db.session, 'View2', ('email','password'))


Если хочется, можно добавить еще одну «вьюшку» и кнопку в шаблон, при нажатии на которую она будет показываться:
from flask.ext.admin import expose

class MyUserAdmin(ModelView):
  # Кнопка будет в шаблоне
  list_template = 'myproject/admin/userlist.html'

  @expose('/report/<int:id>/')
  def report(self, id):
    # Логика тут
    return self.render('myproject/admin/userreport.html', id=id)

  def __init__(self, session):
    super(MyUserAdmin, self).__init__(User, session)

admin = Admin(app)
admin.add_view(MyUserAdmin(db.session))


Для того, что бы получить «нормальный» look and feel, в шаблонах нужно унаследоваться от 'admin/master.html':
{% extends 'admin/master.html' %}
{% block body %}
    Hello World from MyView!
{% endblock %}

Поведение шаблонов и view методов полностью аналогично стандартным из Flask'а.

Расширение функционала


Расширение функционала можно разделить на две части:
1. Изменение поведения встроенных «батареек»
2. Написание чего-то нового

Батарейки

Архитектурно, scaffolding моделей состоит из двух слоев:
1. Уровень доступа к данным. Тут находится логика взаимодействия с конкретной реализацией ORM — от интроспекции модели до методов доступа к данным
2. Уровень UI и остальной логики

Комбинируя оба уровня (через наследование), получаем готовую «батарейку» для конкретной ORM. Нужно поддержать, скажем, mongo-alchemy — пишем логику, наследуемся от базового класса, получаем CRUD для mongo.

ModelView умеет конфигурировать себя через свойства класса (или экземпляра, если прописали в конструкторе). Однако, если готовых возможностей настройки не хватает — всегда можно унаследоваться, переопределить нужные методы и получить совершенно другое поведение.

Аналогично работает файловый менеджер. Например, если нужно запретить доступ к директории reports для пользователя Mike, сделать можно как-то так:
class MyFileAdmin(FileAdmin):
  def is_accessible_path(self, path):
    if path.startswith('reports'):
      return user.login != 'mike'

    return True


Новый функционал

Теперь о добавлении совершенно нового функционала. Вот так добавляется новый «кирпичик»:
from flask.ext.admin import Admin, BaseView, expose

class MyView(BaseView):
  @expose('/')
  def index(self):
    return self.render('myproject/admin/index.html')

admin = Admin(app)
admin.add_view(MyView(name='Hello'))


В меню появится пункт и при открытии пункта 'Hello' вызовется вьюшка index. Выглядит так:


Для генерации ссылок между вьюшками, можно использовать обычный url_for с точкой в начале имени вьюшки:

from flask import url_for

class MyView(BaseView):
  @expose('/')
  def index(self):
    url = url_for('.help')
    return self.render('myproject/admin/index.html', url=url)

  @expose('/help/')
  def help(self):
    return self.render('myproject/admin/help.html', id=id)


Дальше разработка ничем не отличается от написания обычного кода под Flask.

Итого


На текущий момент API библиотеки более-менее стабилизировалось и успешно используется в нескольких проектах.

Примеры лежат тут: github.com/mrjoes/flask-admin/tree/master/examples
Документация тут: flask-admin.readthedocs.org/en/latest

И, как обычно, патчи всегда приветствуются.
Сергей @Joes
карма
52,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (22)

  • 0
    Спасибо за статью и труд, симпатично.
    Вызывает желание ознакомиться с Flask как таковым. Можете вкратце осветить его основные ± по сравнению с Django?
    • +6
      Я бы назвал такие:
      1. Меньший размер, мало зависимостей, можно быстро писать приложения в одном .py файле
      2. Отдельные компоненты из которых состоит Flask лучше аналогов из Django
      3. Explicit is better than implicit, никаких автоимпортов и другой магии кроме threadlocals (которые Django тоже использует)

      Из минусов:
      1. Надо привыкнуть к чтению документации из кучи разных мест (запросы — Werkzeug, шаблоны — Jinja2, ORM скорее всего SQLAlchemy и так далее).
      2. Сторонних компонент явно меньше
      3. Начинающим сложнее — слишком много свободы и непонятно что делать

      Как-то так.
      • 0
        А чем он глобально отличается от того же web.py?
        • +2
          Я когда-то щупал web.py.

          У него другая идеология — будем комбайном а-ля Django, что бы все было из коробки. Своя ORM, свои шаблоны и т.д. А в результате, для проектов больше одного файла, все равно используют сторонние библиотеки.

          Грубо говоря, это попытка быть Django без ресурсов и community Django. Может оно и не так, но внешне очень похоже.
  • +1
    >Отдельные компоненты из которых состоит Flask лучше аналогов из Django
    Ээээ, а можете привести примеры?
    >… кроме threadlocals (которые Django тоже использует)
    Использование threadlocals в джанге считается очень неудачной практикой (причём считается его создателями). Но тут часто действует принцип «если нельзя, но очень хочется...»
    • +5
      > Ээээ, а можете привести примеры?
      Конечно. Там всего две зависимости: Werkzeug и Jinja2 и очень тонкая прослойка между ними.

      Werkzeug умеет много всякого хорошего, например вот такой вот замечательный, интерактивный дебаггер: werkzeug.pocoo.org/docs/debug/

      Jinja2, же, очень хороший шаблонизатор с синтаксисом Django. Лучше хотя бы тем, что при ошибках в шаблонах умеет показывать stacktrace и банально работает быстрее.

      Если взять третью компоненту — ORM, то это скорее всего будет SQLAlchemy (некоторые еще Peewee используют, я его даже не смотрел). А алхимия сама по себе лучше ORM Django. Можно спорить о удобности и привычности синтаксиса, но как ни крутить — позволяет больше и не заставляет писать сырой SQL в относительно сложных случаях.

      Тут скорее дело в том, что Django это комбайн все в одном. Разработчики распыляют свои усилия по достаточно большой кодовой базе. И в каждой своей части, Django работает хоть и не плохо, но хуже чем отдельные специализированные решения. Да, можно сказать что у Django лучше связанность компонент, но на самом деле Flask от слабой связанности не сильно страдает.
      • 0
        peewee кстати может и зря порпускаем — мельком смотрел, вроде неплохо. Хотя вот админка приколоченная к орму, эту болезнь уже вроде проходили.
      • 0
        Дебаггер из werkzeug легко и к джанге правда приколачивается. runplus, или как там, давно не писал под. А вот джинджу с большими трудами, и стандартные компоненты все равно рисуют через джангу, и в общем возможны неудобства.

        У Flask мне кажется не просто компоненты — сам принцип построения удачнее.
    • +4
      threadlocals не очень верно. На самом деле, при нырянии в код джанги и werkzeug, становится понятно, что flask под gevent можно запускать смело, django лучше не стоит — werkzeug учитывает гринлеты, и делает для них специальные обертки.
      Рыбу фугу тоже жрать опасно, но если руки у повара прямые, то можно.

      Ну в качестве примера можно привести sqlalchemy, хоть она и не компонент именно фласка. Jinja2 — ну тоже как бы отдельно можно использовать, но в общем джанговские темплейты она обходит и по скорости и по фичастости/гибкости.
      Flask-Admin вот еще например — заложенная гибкость действительно на хорошем уровне. Ну и в общем все что под фласк написано, оно несколько с другой философией идет, более питоник, более модульное.

      После знакомства с фласком у меня получается писать с минимумом магии, и при этом весьма и весьма прикладисто.
  • +3
    Пользуюсь этой штукой еще в бытность ее админкой в svarga. С переносом по flask стала еще лучше.
    То есть моя личная рекомендация очень пристально смотреть и проникнуться.
    • +3
      Кстати, что со Сваргой — померла, как и большинство микрофреймворков на WZ с появлением Flask?
      • +2
        Ага, была заморожена.

        Сварга, идеологически, была очень похожа на Flask. Ну или наоборот, Flask появился на пол года позже. Те же идеи с threadlocals, та же основа — Werkzeug, Jinja2. Разница только в том, что у Сварги было много идей позаимствовано из Django и идеи эти были не самыми лучшими. Например — структура приложений, автоимпорт моделей и т.д.

        Когда анонсировали Flask, стало сразу понятно что его будет использовать больше народа чисто за счет большего community pocoo. И было принято решение заморозить Сваргу и потихоньку перетаскивать наработки во Flask. Что, собственно, и происходит.
  • +2
    Спасибо, люблю Фласк и давно пользуюсь вашей админкой!
  • +1
    Огромное спасибо за Ваш труд. Месяц назад начал использовать flask-admin и был приятно удивлен гибкостью. Я думаю, что bootstrap здесь очень к месту. В целом нравится намного больше, чем админка в django.
  • 0
    вобще приятно что наконец начали появлятся такие штуки.
    а то даже я от лени предпочитаю джангу — лижбы с админкой не парится
  • +1
    Пользуемся вашей админкой. Допиливал только не очень гибкий is_accessible.
    Большое вам спасибо :)
    • +1
      А что там не работало?
      • +1
        В моем случае надо было при ошибке доступа редиректить на форму логина и сохранять пут для возврата, а не возвращать ошибку. Возможно это не редкая ситуация
        • +3
          Но это просто решилось переопределением метода handle_view + к is_accessible
          def _handle_view(self, name, **kwargs):
                  if not self.is_accessible():
                      #Some Actions
          
        • +4
          А, понял что имеется в виду.

          Да, можно сделать так:
              def _handle_view(self, name, **kwargs):
                  if not self.is_accessible():
                      return login.current_app.login_manager.unauthorized()
          


          А можно кинуть RequestRedirect(url) из самого is_accessible и произойдет редирект.
  • 0
    Спасибо вам за вашу работу!
  • 0
    Приладил к Pylons через middleware.
    Спасибо, очень симпатично и удобно.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.