나는 그것이 어떻게 작동하는지 또는 그것이 원래의 방식으로 설계된 이유를 완전히 이해하지 않고 한동안 요청 / 응용 프로그램 컨텍스트를 사용하고 있습니다. “스택”의 목적은 요청 또는 애플리케이션 컨텍스트와 관련하여 무엇입니까? 이 두 개의 별도 스택입니까, 아니면 둘 다 한 스택의 일부입니까? 요청 컨텍스트가 스택으로 푸시됩니까, 아니면 스택 자체입니까? 여러 컨텍스트를 서로 밀거나 팝할 수 있습니까? 그렇다면 왜 그렇게하고 싶습니까?
모든 질문에 대해 죄송하지만 요청 컨텍스트 및 응용 프로그램 컨텍스트에 대한 설명서를 읽은 후에도 여전히 혼란 스럽습니다.
답변
여러 앱
Flask에 여러 개의 앱이있을 수 있다는 사실을 알기 전까지는 애플리케이션 컨텍스트와 그 목적이 혼란 스럽습니다. 단일 WSGI Python 인터프리터가 여러 Flask 응용 프로그램을 실행하려는 상황을 상상해보십시오. 여기서는 블루 프린트를 말하는 것이 아니라 완전히 다른 플라스크 애플리케이션을 말하는 것입니다.
“Application Dispatching” 예제 의 Flask documentation 섹션 과 유사하게이를 설정할 수 있습니다 .
from werkzeug.wsgi import DispatcherMiddleware
from frontend_app import application as frontend
from backend_app import application as backend
application = DispatcherMiddleware(frontend, {
'/backend': backend
})
“프론트 엔드”와 “백엔드”라는 두 개의 완전히 다른 Flask 응용 프로그램이 있습니다. 즉, Flask(...)
애플리케이션 생성자가 두 번 호출되어 Flask 애플리케이션의 두 인스턴스가 작성되었습니다.
문맥
Flask로 작업 할 때 종종 전역 변수를 사용하여 다양한 기능에 액세스하게됩니다. 예를 들어, 아마도 다음과 같은 코드가있을 것입니다.
from flask import request
그런 다음보기 중에 request
현재 요청 정보에 액세스하는 데 사용할 수 있습니다. 분명히 request
일반적인 전역 변수는 아닙니다. 실제로는 컨텍스트 로컬 값입니다. 다시 말해, “내가 호출 할 때 CURRENT 요청 의 객체 로부터 속성을 request.path
얻는다 “는 등의 마술이있다 . 두 개의 다른 요청은에 대해 다른 결과를 갖습니다 .path
request
request.path
실제로 여러 스레드로 Flask를 실행하더라도 Flask는 요청 객체를 격리 할 수있을 정도로 똑똑합니다. 그렇게하면 각각 다른 요청을 처리하는 두 스레드가 동시에 request.path
해당 요청에 대한 올바른 정보를 호출 하고 얻을 수 있습니다.
그것을 함께 넣어
따라서 우리는 이미 Flask가 동일한 인터프리터에서 여러 응용 프로그램을 처리 할 수 있으며 Flask를 사용하여 “컨텍스트 로컬”글로벌을 사용할 수 있기 때문에 “현재” 요청 이 무엇인지 결정하는 메커니즘이 있어야합니다 ( request.path
) 와 같은 작업을 수행 합니다.
이러한 아이디어를 종합하면 Flask는 “현재”응용 프로그램이 무엇인지 확인할 방법이 있어야합니다.
다음과 유사한 코드가있을 수도 있습니다.
from flask import url_for
이 request
예와 같이 url_for
함수에는 현재 환경에 따라 다른 논리가 있습니다. 그러나이 경우 논리가 어떤 앱이 “현재”앱으로 간주되는지에 크게 의존한다는 것을 알 수 있습니다. 위에 표시된 프런트 엔드 / 백엔드 예제에서 “프론트 엔드”및 “백엔드”앱은 모두 “/ 로그인”경로를 가질 수 있으므로 url_for('/login')
뷰가 프런트 엔드 또는 백엔드 앱에 대한 요청을 처리하는지에 따라 다른 것을 반환해야합니다.
질문에 대답하려면 …
“스택”의 목적은 요청 또는 애플리케이션 컨텍스트와 관련하여 무엇입니까?
요청 컨텍스트 문서에서 :
요청 컨텍스트는 내부적으로 스택으로 유지 관리되므로 여러 번 푸시 및 팝할 수 있습니다. 내부 리디렉션과 같은 것을 구현하는 데 매우 편리합니다.
다시 말해, 일반적으로 이러한 “현재”요청 또는 “현재”응용 프로그램 스택에 0 개 또는 1 개의 항목이 있지만 더 많은 것을 가질 수 있습니다.
주어진 예는 요청이 “내부 리디렉션”의 결과를 반환하도록하는 위치입니다. 사용자가 A를 요청하지만 사용자 B에게 되돌아 가고 싶다고 가정 해 보겠습니다. 대부분의 경우 사용자에게 리디렉션을 발행하고 사용자를 리소스 B로 지정하면 사용자가 B를 가져 오기 위해 두 번째 요청을 실행하게됩니다. 이를 처리하는 약간 다른 방법은 내부 경로 재 지정을 수행하는 것입니다. 즉, A를 처리하는 동안 Flask는 자원 B에 대한 새 요청을 작성하고이 두 번째 요청의 결과를 사용자의 원래 요청 결과로 사용합니다.
이 두 개의 별도 스택입니까, 아니면 둘 다 한 스택의 일부입니까?
그것들은 두 개의 분리 된 스택 입니다. 그러나 이것은 구현 세부 사항입니다. 더 중요한 것은 스택이 너무 많지 않지만 언제든지 “현재”앱 또는 요청을 얻을 수 있다는 사실입니다 (스택 상단).
요청 컨텍스트가 스택으로 푸시됩니까, 아니면 스택 자체입니까?
“요청 컨텍스트”는 “요청 컨텍스트 스택”의 한 항목입니다. “앱 컨텍스트”및 “앱 컨텍스트 스택”과 유사합니다.
여러 컨텍스트를 서로 밀거나 팝할 수 있습니까? 그렇다면 왜 그렇게하고 싶습니까?
Flask 응용 프로그램에서는 일반적으로이 작업을 수행하지 않습니다. 내부 리디렉션의 예는 위에서 설명한 것입니다. 그러나이 경우에도 Flask가 새로운 요청을 처리하게되므로 Flask는 모든 푸시 / 팝핑을 수행하게됩니다.
그러나 스택을 직접 조작하려는 경우가 있습니다.
요청 외부에서 코드 실행
사람들이 가지고있는 전형적인 문제 중 하나는 Flask-SQLAlchemy 확장을 사용하여 아래에 표시된 것과 같은 코드를 사용하여 SQL 데이터베이스 및 모델 정의를 설정한다는 것입니다.
app = Flask(__name__)
db = SQLAlchemy() # Initialize the Flask-SQLAlchemy extension object
db.init_app(app)
그런 다음 셸에서 실행해야하는 스크립트에서 app
및 db
값 을 사용합니다 . 예를 들어 “setup_tables.py”스크립트는 …
from myapp import app, db
# Set up models
db.create_all()
이 경우 Flask-SQLAlchemy 확장 app
프로그램 은 응용 프로그램 에 대해 알고 있지만 create_all()
응용 프로그램 컨텍스트가 없다는 불평 오류가 발생합니다. 이 오류는 정당화됩니다. create_all
메소드를 실행할 때 어떤 애플리케이션을 처리해야하는지 Flask에게 말한 적이 없습니다 .
with app.app_context()
뷰에서 유사한 함수를 실행할 때 왜이 호출 이 필요하지 않은지 궁금 할 것 입니다. Flask는 실제 웹 요청을 처리 할 때 이미 응용 프로그램 컨텍스트 관리를 처리하기 때문입니다. 문제는 실제로 일회용 스크립트에서 모델을 사용할 때와 같이 이러한 뷰 함수 (또는 다른 콜백) 외부에서만 발생합니다.
해결 방법은 응용 프로그램 컨텍스트를 직접 푸시하는 것입니다.
from myapp import app, db
# Set up models
with app.app_context():
db.create_all()
이렇게하면 새로운 응용 프로그램 컨텍스트가 적용됩니다 (의 응용 프로그램을 사용하면 app
둘 이상의 응용 프로그램이있을 수 있음을 기억하십시오).
테스팅
스택을 조작하려는 또 다른 경우는 테스트입니다. 요청을 처리하는 단위 테스트를 작성하고 결과를 확인할 수 있습니다.
import unittest
from flask import request
class MyTest(unittest.TestCase):
def test_thing(self):
with app.test_request_context('/?next=http://example.com/') as ctx:
# You can now view attributes on request context stack by using `request`.
# Now the request context stack is empty
답변
이전 답변은 이미 요청하는 동안 플라스크 배경에서 진행되는 작업에 대한 훌륭한 개요를 제공합니다. 아직 읽지 않았다면 이것을 읽기 전에 @MarkHildreth의 답변을 권장합니다. 요컨대, 각각의 http 요청에 대해 새로운 컨텍스트 (스레드)가 생성 Local
되므로 request
and와 같은 객체를 허용 하는 스레드 기능이 필요합니다.g
요청 특정 컨텍스트를 유지하면서 스레드 전체에서 액세스 할 수 있습니다. 또한 http 요청을 처리하는 동안 Flask는 내부에서 추가 요청을 에뮬레이트 할 수 있으므로 해당 컨텍스트를 스택에 저장해야합니다. 또한 Flask를 사용하면 단일 프로세스 내에서 여러 wsgi 응용 프로그램을 서로 실행할 수 있으며 요청하는 동안 하나 이상의 작업을 호출하여 호출 할 수 있습니다 (각 요청은 새로운 응용 프로그램 컨텍스트를 생성 함). 따라서 응용 프로그램에 대한 컨텍스트 스택이 필요합니다. 그것은 이전 답변에서 다룬 내용에 대한 요약입니다.
내 목표는 이제 설명하여 우리의 현재 이해를 보완하는 방법 플라스크와 WERKZEUG들이 이러한 상황에 맞는 지역 주민과 함께해야합니까 것. 논리를 더 잘 이해하기 위해 코드를 단순화했지만이 정보를 얻으면 실제 소스 ( werkzeug.local
및 flask.globals
) 에있는 대부분의 내용을 쉽게 파악할 수 있습니다 .
Werkzeug가 스레드 로컬을 구현하는 방법을 먼저 이해합시다.
현지
http 요청이 들어 오면 단일 스레드 컨텍스트 내에서 처리됩니다. http 요청 중에 새로운 컨텍스트를 생성하기위한 대안으로 Werkzeug는 일반 스레드 대신 그린 릿 (일부 더 가벼운 “마이크로 스레드”)을 사용할 수 있습니다. Greenlet이 설치되어 있지 않으면 대신 스레드 사용으로 되돌아갑니다. 이 스레드 (또는 그린 릿) 각각은 고유 한 ID로 식별 할 수 있으며 모듈의 get_ident()
기능으로 검색 할 수 있습니다 . 그 기능을 가진 뒤에 마법의 시작 지점입니다 request
, current_app
, url_for
, g
, 및 기타 상황에 바인딩 전역 개체.
try:
from greenlet import get_ident
except ImportError:
from thread import get_ident
이제 우리는 identity 함수를 사용하여 주어진 시간에 어떤 스레드를 사용하고 있는지를 알 Local
수 있으며 전역 적으로 액세스 할 수있는 컨텍스트 객체 인 thread를 만들 수 있습니다. 그러나 속성에 액세스 할 때 속성은 해당 값으로 확인됩니다 그 특정 스레드. 예 :
# globally
local = Local()
# ...
# on thread 1
local.first_name = 'John'
# ...
# on thread 2
local.first_name = 'Debbie'
두 값 모두 전역 적으로 액세스 가능한 Local
객체에 동시에 존재 하지만 local.first_name
스레드 1의 컨텍스트 내에서 액세스 하면을 제공 'John'
하지만 'Debbie'
스레드 2 에서는 반환 됩니다.
어떻게 가능합니까? 단순화 된 코드를 살펴 보자.
class Local(object)
def __init__(self):
self.storage = {}
def __getattr__(self, name):
context_id = get_ident() # we get the current thread's or greenlet's id
contextual_storage = self.storage.setdefault(context_id, {})
try:
return contextual_storage[name]
except KeyError:
raise AttributeError(name)
def __setattr__(self, name, value):
context_id = get_ident()
contextual_storage = self.storage.setdefault(context_id, {})
contextual_storage[name] = value
def __release_local__(self):
context_id = get_ident()
self.storage.pop(context_id, None)
local = Local()
위의 코드에서 우리는 get_ident()
현재 그린 릿 또는 스레드를 식별하는 마법이 끓는 것을 볼 수 있습니다 . Local
저장소는 단지 현재 스레드에 모든 데이터 콘텐츠를 저장하기위한 키로서 그것을 이용한다.
여러 가질 수 Local
프로세스 당 객체를하고 request
, g
, current_app
및 다른 사람은 간단하게 그렇게 만들 수 있었다. 그러나 이것이 기술적으로 Local
객체가 아니라보다 정확하게 LocalProxy
객체 인 Flask에서 수행 된 방식은 아닙니다 . 무엇입니까 LocalProxy
?
로컬 프록시
LocalProxy는 a Local
를 쿼리하여 관심있는 다른 객체 (즉 프록시하는 객체)를 찾는 객체입니다. 이해해 봅시다 :
class LocalProxy(object):
def __init__(self, local, name):
# `local` here is either an actual `Local` object, that can be used
# to find the object of interest, here identified by `name`, or it's
# a callable that can resolve to that proxied object
self.local = local
# `name` is an identifier that will be passed to the local to find the
# object of interest.
self.name = name
def _get_current_object(self):
# if `self.local` is truly a `Local` it means that it implements
# the `__release_local__()` method which, as its name implies, is
# normally used to release the local. We simply look for it here
# to identify which is actually a Local and which is rather just
# a callable:
if hasattr(self.local, '__release_local__'):
try:
return getattr(self.local, self.name)
except AttributeError:
raise RuntimeError('no object bound to %s' % self.name)
# if self.local is not actually a Local it must be a callable that
# would resolve to the object of interest.
return self.local(self.name)
# Now for the LocalProxy to perform its intended duties i.e. proxying
# to an underlying object located somewhere in a Local, we turn all magic
# methods into proxies for the same methods in the object of interest.
@property
def __dict__(self):
try:
return self._get_current_object().__dict__
except RuntimeError:
raise AttributeError('__dict__')
def __repr__(self):
try:
return repr(self._get_current_object())
except RuntimeError:
return '<%s unbound>' % self.__class__.__name__
def __bool__(self):
try:
return bool(self._get_current_object())
except RuntimeError:
return False
# ... etc etc ...
def __getattr__(self, name):
if name == '__members__':
return dir(self._get_current_object())
return getattr(self._get_current_object(), name)
def __setitem__(self, key, value):
self._get_current_object()[key] = value
def __delitem__(self, key):
del self._get_current_object()[key]
# ... and so on ...
__setattr__ = lambda x, n, v: setattr(x._get_current_object(), n, v)
__delattr__ = lambda x, n: delattr(x._get_current_object(), n)
__str__ = lambda x: str(x._get_current_object())
__lt__ = lambda x, o: x._get_current_object() < o
__le__ = lambda x, o: x._get_current_object() <= o
__eq__ = lambda x, o: x._get_current_object() == o
# ... and so forth ...
이제 전 세계적으로 접근 가능한 프록시를 만들려면
# this would happen some time near application start-up
local = Local()
request = LocalProxy(local, 'request')
g = LocalProxy(local, 'g')
이제 요청이 진행되는 동안 어느 시점에 어떤 스레드에 관계없이 이전에 만든 프록시가 액세스 할 수있는 일부 객체를 로컬에 저장합니다
# this would happen early during processing of an http request
local.request = RequestContext(http_environment)
local.g = SomeGeneralPurposeContainer()
스스로 LocalProxy
액세스하는 대신 전역 액세스 가능한 개체로 사용 하면 개체 Locals
관리가 간소화된다는 이점이 있습니다. Local
전역 적으로 액세스 가능한 많은 프록시를 만들려면 단일 개체 만 있으면 됩니다. 요청이 끝나면 정리하는 동안 단순히 하나를 해제하고 Local
(즉, 저장소에서 context_id를 팝) 프록시에 신경 쓰지 않고 여전히 전역 적으로 액세스 할 수 있으며 여전히 Local
객체를 찾기 위해 연기 합니다. 후속 http 요청에 관심이 있습니다.
# this would happen some time near the end of request processing
release(local) # aka local.__release_local__()
Werkzeug는 이미을 LocalProxy
가지고있을 때 의 생성을 단순화하기 위해 다음과 같이 마술 방법을 Local
구현합니다 Local.__call__()
.
class Local(object):
# ...
# ... all same stuff as before go here ...
# ...
def __call__(self, name):
return LocalProxy(self, name)
# now you can do
local = Local()
request = local('request')
g = local('g')
그러나, 당신은 어떻게 아직도의 플라스크 소스 (flask.globals)에 보면 request
, g
, current_app
및 session
생성됩니다. 우리가 확립 한대로 Flask는 하나의 실제 http 요청에서 여러 개의 “가짜”요청을 생성 할 수 있으며 그 과정에서 여러 응용 프로그램 컨텍스트를 푸시 할 수도 있습니다. 이것은 일반적인 사용 사례는 아니지만 프레임 워크의 기능입니다. 이러한 “동시”요청 및 앱은 여전히 ”초점”을 가진 하나만 실행되도록 제한되기 때문에 각각의 컨텍스트에 스택을 사용하는 것이 좋습니다. 새 요청이 생성되거나 응용 프로그램 중 하나가 호출 될 때마다 해당 스택의 맨 위에 컨텍스트를 푸시합니다. 플라스크는 LocalStack
이를 위해 객체를 사용 합니다. 그들이 사업을 마치면 스택에서 맥락을 드러냅니다.
LocalStack
이것은 LocalStack
코드 의 모양을 이해하기 위해 코드가 단순화 된 모습입니다.
class LocalStack(object):
def __init__(self):
self.local = Local()
def push(self, obj):
"""Pushes a new item to the stack"""
rv = getattr(self.local, 'stack', None)
if rv is None:
self.local.stack = rv = []
rv.append(obj)
return rv
def pop(self):
"""Removes the topmost item from the stack, will return the
old value or `None` if the stack was already empty.
"""
stack = getattr(self.local, 'stack', None)
if stack is None:
return None
elif len(stack) == 1:
release_local(self.local) # this simply releases the local
return stack[-1]
else:
return stack.pop()
@property
def top(self):
"""The topmost item on the stack. If the stack is empty,
`None` is returned.
"""
try:
return self.local.stack[-1]
except (AttributeError, IndexError):
return None
위에서 a LocalStack
는 스택에 저장된 로컬 그룹이 아니라 로컬에 저장된 스택입니다. 이것은 스택에 전역 적으로 액세스 할 수 있지만 각 스레드에서 다른 스택이라는 것을 의미합니다.
플라스크가없는 request
, current_app
, g
, 그리고 session
A를 직접 해결하는 객체 LocalStack
, 오히려 사용하여 LocalProxy
객체를 그 랩 조회 기능 (대신의 Local
로부터 기본 개체를 찾을 수 객체) LocalStack
:
_request_ctx_stack = LocalStack()
def _find_request():
top = _request_ctx_stack.top
if top is None:
raise RuntimeError('working outside of request context')
return top.request
request = LocalProxy(_find_request)
def _find_session():
top = _request_ctx_stack.top
if top is None:
raise RuntimeError('working outside of request context')
return top.session
session = LocalProxy(_find_session)
_app_ctx_stack = LocalStack()
def _find_g():
top = _app_ctx_stack.top
if top is None:
raise RuntimeError('working outside of application context')
return top.g
g = LocalProxy(_find_g)
def _find_app():
top = _app_ctx_stack.top
if top is None:
raise RuntimeError('working outside of application context')
return top.app
current_app = LocalProxy(_find_app)
이들 모두는 응용 프로그램 시작시 선언되지만 요청 컨텍스트 또는 응용 프로그램 컨텍스트가 해당 스택으로 푸시 될 때까지 실제로는 아무것도 해결하지 않습니다.
컨텍스트에 실제로 스택이 삽입되는 방법과 궁금한 점이 궁금하다면 flask.app.Flask.wsgi_app()
wsgi 앱의 시작 지점 (즉, 웹 서버가 호출하고 http 환경을 전달할 대상)을 확인하십시오. 요청)에 제공하고,의 생성에 따라 RequestContext
모든 후속 통해 객체를 push()
에 _request_ctx_stack
. 스택 상단으로 밀면을 통해 액세스 할 수 있습니다 _request_ctx_stack.top
. 흐름을 보여주기위한 약식 코드는 다음과 같습니다.
따라서 앱을 시작하고 WSGI 서버에서 사용할 수있게합니다.
app = Flask(*config, **kwconfig)
# ...
나중에 http 요청이 들어오고 WSGI 서버는 일반적인 매개 변수로 앱을 호출합니다 …
app(environ, start_response) # aka app.__call__(environ, start_response)
이것은 대략 앱에서 발생하는 것입니다 …
def Flask(object):
# ...
def __call__(self, environ, start_response):
return self.wsgi_app(environ, start_response)
def wsgi_app(self, environ, start_response):
ctx = RequestContext(self, environ)
ctx.push()
try:
# process the request here
# raise error if any
# return Response
finally:
ctx.pop()
# ...
그리고 이것은 대략 RequestContext에서 일어나는 일입니다 …
class RequestContext(object):
def __init__(self, app, environ, request=None):
self.app = app
if request is None:
request = app.request_class(environ)
self.request = request
self.url_adapter = app.create_url_adapter(self.request)
self.session = self.app.open_session(self.request)
if self.session is None:
self.session = self.app.make_null_session()
self.flashes = None
def push(self):
_request_ctx_stack.push(self)
def pop(self):
_request_ctx_stack.pop()
요청 초기화가 완료되었다고 가정하면 request.path
뷰 함수 중 하나 에서 조회가 다음과 같이 진행됩니다.
- 전역 적으로 접근 가능한
LocalProxy
객체 에서 시작 합니다request
. - 기본 관심 객체 (프록시 대상 객체)를 찾으려면 조회 함수
_find_request()
(로 등록 된 함수)를 호출합니다self.local
. - 해당 함수 는 스택의 최상위 컨텍스트에 대해
LocalStack
객체_request_ctx_stack
를 쿼리합니다 . - 최상위 컨텍스트를 찾기 위해
LocalStack
객체는 먼저 내부Local
속성 (self.local
)에stack
이전에 저장된 속성을 쿼리합니다 . stack
그것 에서 그것은 최고 컨텍스트를 얻는다- 및
top.request
따라서 관심의 기본 개체로 확인됩니다. - 그 객체에서 우리는
path
속성 을 얻습니다
우리가 방법을 살펴 보았다 그래서 Local
, LocalProxy
그리고 LocalStack
일을, 이제 검색의 의미와 뉘앙스의 순간에 대한 생각 path
에서 :
request
세계적으로 간단한 접근 객체가 될 것이다 객체입니다.request
로컬 될 객체입니다.request
목적은 지역의 속성으로서 저장된다.request
로컬에 저장된 객체에 대한 프록시 객체.request
차례로 로컬에 저장되어있는 스택에 저장된 객체.request
로컬에 저장된 스택 객체에 대한 프록시 객체. <-Flask가하는 일입니다.
답변
@ Mark Hildreth 의 답변은 거의 없습니다 .
같은 컨텍스트 스택 모양 {thread.get_ident(): []}
, []
사용했기 때문에 “스택”에만 호출 append
( push
) pop
와 [-1]
( __getitem__(-1)
) 작업. 따라서 컨텍스트 스택은 스레드 또는 그린 릿 스레드에 대한 실제 데이터를 유지합니다.
current_app
, g
, request
, session
등이다 LocalProxy
단지 특별한 방법을 오버라이드 객체 __getattr__
, __getitem__
, __call__
, __eq__
등 및 컨텍스트 스택 최상부 (의 리턴 값 [-1]
(인수 이름) current_app
, request
예를 들면).
LocalProxy
이 오브젝트를 한 번 가져와야하므로 실제로 놓칠 수 없습니다. 따라서 request
코드의 어느 곳에서나 가져 오는 것이 더 좋으며 대신 함수 및 메소드에 요청 인수를 전송하여 재생하십시오. 당신은 그것으로 자신의 확장을 쉽게 작성할 수 있지만, 방대한 사용법으로 코드를 이해하기가 더 어려워 질 수 있다는 것을 잊지 마십시오.
https://github.com/mitsuhiko/werkzeug/blob/master/werkzeug/local.py 를 이해하는 데 시간을 보내십시오 .
그렇다면 두 스택을 모두 채우는 방법은 무엇입니까? 요청시 Flask
:
request_context
환경별로 생성 (initmap_adapter
, match path)- 이 요청을 입력하거나 푸시하십시오.
- 이전 지우기
request_context
app_context
누락되어 응용 프로그램 컨텍스트 스택으로 푸시되면 작성- 이 요청은 컨텍스트 스택을 요청하도록 푸시되었습니다.
- 누락 된 경우 초기화 세션
- 이전 지우기
- 파견 요청
- 요청을 지우고 스택에서 팝
답변
예를 들어, 사용자 컨텍스트를 설정한다고 가정하십시오 (Local 및 LocalProxy의 플라스크 구성 사용).
하나의 사용자 클래스를 정의하십시오.
class User(object):
def __init__(self):
self.userid = None
현재 스레드 또는 Greenlet 내에서 사용자 오브젝트를 검색하는 함수를 정의하십시오.
def get_user(_local):
try:
# get user object in current thread or greenlet
return _local.user
except AttributeError:
# if user object is not set in current thread ,set empty user object
_local.user = User()
return _local.user
이제 LocalProxy를 정의하십시오
usercontext = LocalProxy(partial(get_user, Local()))
현재 스레드에서 사용자의 사용자 ID를 얻으려면 usercontext.userid
설명 :
1. Local은 identity 및 objet을 가지며, identity는 threadid 또는 greenlet id입니다.이 예에서 _local.user = User ()는 _local .___ storage __ [현재 스레드의 id] [ “user”] = User ()와 동일합니다.
- LocalProxy는 로컬 오브젝트를 랩핑하도록 조작을 위임 하거나 대상 오브젝트를 리턴하는 함수를 제공 할 수 있습니다. 위 예제에서 get_user 함수는 현재 사용자 객체를 LocalProxy에 제공하고 usercontext.userid로 현재 사용자의 사용자 ID를 요청할 때 LocalProxy의 __getattr__ 함수는 먼저 get_user를 호출하여 User 객체 (user)를 가져온 다음 getattr (user, “userid”)를 호출합니다. User (현재 스레드 또는 Greenlet)에서 userid를 설정하려면 다음을 수행하십시오. usercontext.userid = “user_123”