Когда следует использовать 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.