Warning: include(/volume1/web/cyberhost.biz/wp-content/plugins/jaster_cahce/cache/top-cache.php): failed to open stream: No such file or directory in /volume1/web/cyberhost.biz/index.php on line 9 Call Stack: 0.0001 356272 1. {main}() /volume1/web/cyberhost.biz/index.php:0 Warning: include(): Failed opening '/volume1/web/cyberhost.biz/wp-content/plugins/jaster_cahce/cache/top-cache.php' for inclusion (include_path='.:/usr/share/pear') in /volume1/web/cyberhost.biz/index.php on line 9 Call Stack: 0.0001 356272 1. {main}() /volume1/web/cyberhost.biz/index.php:0 What the flask? | Хостинг за 90 р. от cyberhost.biz — платный хостинг
+7 993 930-19-90 suport@cyberhost.biz


Вообще-то, это картинка от wtforms, но у меня гимп почему-то не запускается.

Эту статью я пишу в баре. Очень хочется похоливарить, но бармен на меня смотрит круглыми глазами, а кальянщик просто улыбается и мотает головой.

Однажды, меня спросили: что плохого во flask? Тогда меня полностью устраивал этот милый фреймворк. Поработав с ним какое-то время, я написал все, что думаю, в рабочий слак, на что мне ответили: "Мурад, будь добрее". Вообще, я добрый и пушистый, но wtf?!

Стоит отметить, что я являюсь большим поклонником работы Армина. Его пакеты используются во многих мои проектах. А еще он невероятная заноза в сообществе Python. И это хорошо.

Flask

Flask — это обертка над очень крутыми обособленными проектами. Некоторые сравнивают его с джангой. Я хз почему.

Если попытаться описать все проблемы фласка в двух пунктах:

  • импорты
  • контекст реквеста
  • Все. Дальше можно не читать. Но если все еще не понятно, листаем дальше.

    Blueprints

    Если в джанге все приложения подключаются в INSTALLED_APPS, то во фласке используется концепция blueprints. Этакое приложение и обособленный неймспейс урлов в одном флаконе:

    from flask import Flask
    from yourapplication.simple_page import simple_page
    app = Flask(__name__)
    app.register_blueprint(simple_page, end_point=’/simple_page’)

    Далее, можно роутить урлы так: url_for(‘simple_page.index’).

    Вложенность

    А ее нет. Нельзя просто так взять и сделать вложенный неймспейс. В сети можно найти "решения", но здесь я буду рассматривать только каробочный фласк, потому что написать-то можно все.

    Импорты

    Когда вы делаете так в продакшен коде:

    if foo == 3:
    do_stuff(foo)

    Где-то в мире грущу я! Берегите меня, выносите это в сеттинги.

    from myapp import app
    class Foo(FlaskForm):
    choices = SelectField(choices=app.conf.FOO_CHOICES)

    Концептуально. Но работать не будет. Потому что пару строчек назад мы импортировали пакет в myapp и навечно заказали себе путь туда.

    Погодите, должен быть выход! Ага!

    from flask import current_app

    И это не работает. Потому что current_app доступен только в контектсте реквеста!

    — Вынеси это наконец в файл сеттингов приложения, больной ублюдок! — голос из зала.
    — А как я буду их подменять на проде или тестовом, а?

    Кстати, для джанги на этот случай у меня есть специальная батарейка.
    Просто представьте себе прекрасный дивный мир, где django.conf.settings доступен только в контексте реквеста!

    flask.g

    Нельзя не пошутить про одноименную точку. А главное, ее не нужно искать, она всегда тут: flask.g. Бада-бум-тсс!
    Вот поэтому Армин — мой кумир!

    В нее можно пробросить все необходимое:

    @app.before_request
    def before_request():
    g.locale = get_locale()
    g.foo = foo

    Однако, это будет работать только в контексте реквеста, как и любые другие магические объекты фласка.

    Роутинг урлов и их методы

    У меня на сайте есть такой кусок урлов:

    bp.add_url_rule(‘/api/v1/’, view_func=ApiView.as_view(‘api_view’))

    bp.add_url_rule(‘/<path:path>/’, view_func=PageView.as_view(‘page_view’))

    ApiView обрабатывает только один метод — POST. Угадайте, что будет если спросить GET? Ага, 404. Ее обеспечивает вторая вьюшка.
    Чтобы получить NOT ALLOWED, нужно явно вернуть 405 в ApiView!

    Fask, что с тобой не так?

    Стейк!

    А. Погодите. Это мне. Омн-омн-омн.

    flask-wtf. CSRF

    Ох. Допустим, нам нужно отключить проверку в одной вьюхе:

    @app.route(‘/foo’, methods=(‘GET’, ‘POST’))
    @csrf.exempt
    def my_handler():
    # …
    return ‘ok’

    Значит, нам нужен app. Помните про импорты, да? Ищем выход, лезем в сорцы:

    def exempt(self, view):

    if isinstance(view, string_types):
    view_location = view
    else:
    view_location = ‘.’.join((view.__module__, view.__name__))
    self._exempt_views.add(view_location)
    return view

    Ура! Можно передать путь до вьюхи (в версии, которая вышла две недели назад)! Пробуем:

    csrf.exempt(‘website.apps.typus_web.views.ApiView’)

    Не работает. На самом деле (ненавижу это выражение), мы подменили имя вьюхи, когда вызывали ApiView.as_view(‘api_view’):

    csrf.exempt(‘website.apps.typus_web.views.api_view’)

    И "все равно", что мы указываем путь до объекта, которого нет. Работает! Не работает.

    А знаете почему? Потому что форма. Она ничегошеньки не знает про вьюху:

    class ApiForm(ViewForm):

    class Meta(ViewForm.Meta):
    csrf = False

    Вот теперь работает.

    url_for()

    Допустим, вы хотите сделать так:

    NAVIGATION = (
    (url_for(‘flatpages:index’), _(‘Home page’)),
    )

    Забудьте. Вне контекста не работает. Наверное, можно сделать свой ленивый объект, в конце-концов, в джанге это тоже не сразу появилось.

    flask-testing

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

    AssertionError: Popped wrong request context.

    Ой. Что-то пошло не так. А знаете что? Я вот тоже не знаю.
    На самом деле (ненавижу это выражение), я схватил NotImplementedError в одном из методов, которые не переопределил. Но поинт в том, что уронив тесты, вам ни за что не понять в чем причина.

    Всякое разное

    В процессе ковыряния фласка, нашел несколько моментов:

    def jsonify(*args, **kwargs):

    if args and kwargs:
    raise TypeError(‘jsonify() behavior undefined when passed both args and kwargs’)
    elif len(args) == 1: # single args are passed directly to dumps()
    data = args[0]
    else:
    data = args or kwargs

    Здесь что-то происходит. Это все, что я понимаю.

    И это:

    def make_response(*args):
    if not args:
    return current_app.response_class()
    if len(args) == 1:
    args = args[0]
    return current_app.make_response(args)

    А теперь ягодки:

    class Flask(_PackageBoundObject):
    def make_response(self, rv):
    status_or_headers = headers = None
    if isinstance(rv, tuple):
    rv, status_or_headers, headers = rv + (None,) * (3 — len(rv))

    Все, жгите!

    P.S. А чего я один эээ статьи пишу, м? Кто хочет на той неделе, скажем, во вторник (чтобы больше вместить) пойти в бар (в Питере, в районе Звездной)?.. Пишите в инбокс.

    P.P.S. Хабр, ты почему не типографишь тексты? Вот, я даже штуку написал!

    Warning: include(/volume1/web/cyberhost.biz/wp-content/plugins/jaster_cahce/cache/bottom-cache.php): failed to open stream: No such file or directory in /volume1/web/cyberhost.biz/index.php on line 13 Call Stack: 0.0001 356272 1. {main}() /volume1/web/cyberhost.biz/index.php:0 Warning: include(): Failed opening '/volume1/web/cyberhost.biz/wp-content/plugins/jaster_cahce/cache/bottom-cache.php' for inclusion (include_path='.:/usr/share/pear') in /volume1/web/cyberhost.biz/index.php on line 13 Call Stack: 0.0001 356272 1. {main}() /volume1/web/cyberhost.biz/index.php:0