Когда следует использовать Flask.g?


174

Я видел, что gв Flask 0.10 из контекста запроса перейдет контекст приложения, что привело меня в замешательство относительно предполагаемого использования g.

Мое понимание (для Flask 0.9) заключается в следующем:

  • g живет в контексте запроса, т. е. создается заново при запуске запроса и доступен до его окончания
  • gпредназначен для использования в качестве «доски запросов», где я могу поместить вещи, относящиеся к продолжительности запроса (то есть установить флаг в начале запроса и обработать его в конце, возможно, из before_request/ after_requestпары)
  • в дополнение к хранению уровня запроса, gможет и должен использоваться для управления ресурсами, т. е. для удержания соединений с базой данных и т. д.

Какие из этих предложений больше не соответствуют действительности в Flask 0.10? Может кто-нибудь указать мне на ресурс, обсуждающий причины изменений? Что я должен использовать в качестве «доски запросов» в Flask 0.10 - я должен создать свой собственный локальный прокси-сервер для конкретного приложения / расширения и поместить его в стек контекста before_request? Какой смысл управления ресурсами в контексте приложения, если мое приложение живет долго (не как запрос) и, следовательно, ресурсы никогда не освобождаются?


Я согласен, это довольно странное изменение. Надеемся, что mitsuhiko реализует какой-то объект контекста запроса для замены gв 0.10, иначе может показаться, что во многих кодах могут появиться некоторые ошибочные ошибки.
Анов

11
FWIW, Армин Ронахер (автор Flask) выпустил продолжение «Advanced Flask Patterns», в котором показан пример кода о том, как использовать новое flask.g. speakerdeck.com/mitsuhiko/advanced-flask-patterns-1
Маркус Унтервадитцер

1
Кроме того, новый контекст запроса подразумевает новый контекст приложения, поэтому он должен нормально работать при обычном использовании
Ронни

Ответы:


120

Усовершенствованные шаблоны колб , связанные с Маркусом , объясняют некоторые изменения gв 0.10:

  • g теперь живет в контексте приложения.
  • Каждый запрос выдвигает новый контекст приложения , стирая старый, поэтому gвсе еще может использоваться для установки флагов для запроса без изменения кода.
  • Контекст приложения появляется после вызова teardown_request. (Презентация Армина объясняет это происходит потому , что такие вещи , как создание соединений БДА являются задачами , которые настройка среды для запроса, и не должна быть обработана внутри before_requestи after_request)

В исходном коде, на который вы ссылаетесь, когда app_ctx is None or app_ctx.app != self.appFalse, старый контекст приложения, кажется, используется повторно? Это кажется неправильным, поскольку контекст приложения «не будет разделен между запросами» ...
nalzok

2
Вы имеете в виду подталкиваниеapp.app_context() ? Если это так, следует отметить, app_context()что каждый новый вызов создает новый контекст приложения - он никогда не использует контекст повторно.
theY4Kman

1
Да, это правда, но когда app_ctx is not None and app_ctx.app == self.app, то app_ctx = self.app.app_context()строка не выполняется; только self._implicit_app_ctx_stack.append(None)выполняется в этом случае.
Нальзок

1
Ой, прости, я неправильно прочитал! В производственных условиях на поток (или гринлет) подается только один запрос. Толкается только один RequestContext, поэтому толкается только один AppContext. Но если включен режим отладки и запрос не выполняется, Flask сохраняет контекст , поэтому его можно использовать с отладчиком . Noneдобавляется к _app_ctx_stack, поэтому, когда запрос сносится, он не знает, пока не AppContextпоявится. То же самое происходит с тестовым клиентом, который сохраняет контекст, чтобы его можно было проверить.
theY4Kman

Таким образом, область действия g относится к запросу (потоку), и он не будет сохранять значение в следующем запросе.
переменная

83

В качестве дополнения к информации в этой теме: меня тоже немного смутило поведение flask.g, но некоторое быстрое тестирование помогло мне прояснить это. Вот что я опробовал:

from flask import Flask, g
app = Flask(__name__)

with app.app_context():
    print('in app context, before first request context')
    print('setting g.foo to abc')
    g.foo = 'abc'
    print('g.foo should be abc, is: {0}'.format(g.foo))

    with app.test_request_context():
        print('in first request context')
        print('g.foo should be abc, is: {0}'.format(g.foo))
        print('setting g.foo to xyz')
        g.foo = 'xyz'
        print('g.foo should be xyz, is: {0}'.format(g.foo))

    print('in app context, after first request context')
    print('g.foo should be abc, is: {0}'.format(g.foo))

    with app.test_request_context():
        print('in second request context')
        print('g.foo should be abc, is: {0}'.format(g.foo))
        print('setting g.foo to pqr')
        g.foo = 'pqr'
        print('g.foo should be pqr, is: {0}'.format(g.foo))

    print('in app context, after second request context')
    print('g.foo should be abc, is: {0}'.format(g.foo))

И вот результат, который это дает:

in app context, before first request context
setting g.foo to abc
g.foo should be abc, is: abc  

in first request context
g.foo should be abc, is: abc
setting g.foo to xyz
g.foo should be xyz, is: xyz  

in app context, after first request context
g.foo should be abc, is: xyz  

in second request context
g.foo should be abc, is: xyz
setting g.foo to pqr
g.foo should be pqr, is: pqr  

in app context, after second request context
g.foo should be abc, is: pqr

Как сказал выше Y4Kman: «Каждый запрос выдвигает новый контекст приложения». И, как говорят в документации Flask , контекст приложения «не будет разделен между запросами». Теперь, что не было явно указано (хотя я предполагаю, что это подразумевается из этих утверждений), и что ясно показывает мое тестирование, это то, что вы никогда не должны явно создавать несколько контекстов запроса, вложенных в один контекст приложения, потому что flask.g(и co) не ' У него нет никакого волшебства, благодаря которому он функционирует на двух разных «уровнях» контекста, причем разные состояния существуют независимо на уровнях приложения и запроса.

Реальность такова , что «контекст приложения» потенциально весьма вводит в заблуждение название, потому что app.app_context() это за запроса контекста , точно так же , как «контекст запроса» . Думайте об этом как о «запросе контекста lite», необходимом только в том случае, если вам нужны некоторые переменные, для которых обычно требуется контекст запроса, но вам не нужен доступ к какому-либо объекту запроса (например, при выполнении пакетных операций с БД в сценарий оболочки). Если вы попытаетесь расширить контекст приложения, чтобы охватить более одного контекста запроса, у вас возникнут проблемы. Итак, вместо моего теста выше, вы должны вместо этого написать код, подобный этому, с контекстами Flask:

from flask import Flask, g
app = Flask(__name__)

with app.app_context():
    print('in app context, before first request context')
    print('setting g.foo to abc')
    g.foo = 'abc'
    print('g.foo should be abc, is: {0}'.format(g.foo))

with app.test_request_context():
    print('in first request context')
    print('g.foo should be None, is: {0}'.format(g.get('foo')))
    print('setting g.foo to xyz')
    g.foo = 'xyz'
    print('g.foo should be xyz, is: {0}'.format(g.foo))

with app.test_request_context():
    print('in second request context')
    print('g.foo should be None, is: {0}'.format(g.get('foo')))
    print('setting g.foo to pqr')
    g.foo = 'pqr'
    print('g.foo should be pqr, is: {0}'.format(g.foo))

Который даст ожидаемые результаты:

in app context, before first request context
setting g.foo to abc
g.foo should be abc, is: abc  

in first request context
g.foo should be None, is: None
setting g.foo to xyz
g.foo should be xyz, is: xyz  

in second request context
g.foo should be None, is: None
setting g.foo to pqr
g.foo should be pqr, is: pqr

7
Утвержденный из-за последнего параграфа, контексты фляги довольно запутаны, чтобы быть понятыми сначала. Исходя из названия, вы чувствуете, что контекст запроса относится к запросу и что контекст приложения существует даже после запроса или не зависит от времени его существования.
Симаначчи
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.