Коли слід використовувати Flask.g?


173

Я бачив, що gперейде від контексту запиту до контексту додатка на Flask 0.10, що збентежило мене щодо використання g.

Я розумію (для колби 0,9):

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

Яке з цих речень більше не відповідає дійсності у колбі 0,10? Чи може хтось вказати мені на ресурс, який обговорює причини зміни? Що я повинен використовувати як "класну дошку запитів" на Flask 0.10 - чи слід створити власний проксі-локальний проксі-локальний проксі та дотиснути його до контекстного стеку before_request? Який сенс управління ресурсами в контексті програми, якщо моя програма живе тривалий час (не як запит), і таким чином ресурси ніколи не звільняються?


Я погоджуюся, це досить дивна зміна. Сподіваємось, mitsuhiko реалізує якийсь контекст запиту контекстного запиту для заміни gв 0.10, інакше це звучить, як багато коду може почати розробляти деякі хибні помилки.
Аноров

11
FWIW, Армін Ронахер (автор Flask) випустив продовження "Розширених шаблонів колби", де показаний приклад коду про те, як використовувати новий flask.g. speakerdeck.com/mitsuhiko/advanced-flask-patterns-1
Markus Unterwaditzer

1
також новий контекст запиту передбачає новий контекст програми, тому він повинен просто працювати нормально при звичайному використанні
Ronny

Відповіді:


119

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

  • g тепер живе в контексті програми.
  • Кожен запит висуває новий контекст програми , витираючи старий, тому він gвсе ще може бути використаний для встановлення прапорів на запит без зміни коду.
  • Контекст програми з'являється після виклику teardown_request. (Презентація Armin пояснює це тим, що такі речі, як створення з'єднань з БД, - це завдання, які встановлюють середовище для запиту, і не повинні оброблятися всередині 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)виконується лише в цьому випадку.
nalzok

1
О, вибачте, я неправильно прочитав! У виробничих умовах є лише один запит, що подається на кожну нитку (або greenlet). Лише один 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

Як сказав theY4Kman вище, "Кожен запит підштовхує новий контекст програми". І як кажуть документи 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
Контексти колби, оприлюднені через останній абзац, спочатку досить заплутані, щоб їх зрозуміти. З назви ви отримуєте відчуття, що контекст запиту є на запит і що контекст програми існує навіть після запиту або на нього не впливає його життя.
simawhat
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.