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

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

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

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

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

Ответ 1

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

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

Ответ 2

В качестве дополнения к информации в этой теме: меня тоже немного смутило поведение 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 (и со) не обладает какой-либо магией, благодаря которой он функционирует на двух разных "уровнях" контекста, причем разные состояния существуют независимо на уровнях приложения и запроса.

Реальность такова, что "контекст приложения" потенциально может вводить в заблуждение имя, поскольку 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