[python] Flask의 컨텍스트 스택의 목적은 무엇입니까?

나는 그것이 어떻게 작동하는지 또는 그것이 원래의 방식으로 설계된 이유를 완전히 이해하지 않고 한동안 요청 / 응용 프로그램 컨텍스트를 사용하고 있습니다. “스택”의 목적은 요청 또는 애플리케이션 컨텍스트와 관련하여 무엇입니까? 이 두 개의 별도 스택입니까, 아니면 둘 다 한 스택의 일부입니까? 요청 컨텍스트가 스택으로 푸시됩니까, 아니면 스택 자체입니까? 여러 컨텍스트를 서로 밀거나 팝할 수 있습니까? 그렇다면 왜 그렇게하고 싶습니까?

모든 질문에 대해 죄송하지만 요청 컨텍스트 및 응용 프로그램 컨텍스트에 대한 설명서를 읽은 후에도 여전히 혼란 스럽습니다.



답변

여러 앱

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얻는다 “는 등의 마술이있다 . 두 개의 다른 요청은에 대해 다른 결과를 갖습니다 .pathrequestrequest.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)

그런 다음 셸에서 실행해야하는 스크립트에서 appdb값 을 사용합니다 . 예를 들어 “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되므로 requestand와 같은 객체를 허용 하는 스레드 기능이 필요합니다.g요청 특정 컨텍스트를 유지하면서 스레드 전체에서 액세스 할 수 있습니다. 또한 http 요청을 처리하는 동안 Flask는 내부에서 추가 요청을 에뮬레이트 할 수 있으므로 해당 컨텍스트를 스택에 저장해야합니다. 또한 Flask를 사용하면 단일 프로세스 내에서 여러 wsgi 응용 프로그램을 서로 실행할 수 있으며 요청하는 동안 하나 이상의 작업을 호출하여 호출 할 수 있습니다 (각 요청은 새로운 응용 프로그램 컨텍스트를 생성 함). 따라서 응용 프로그램에 대한 컨텍스트 스택이 필요합니다. 그것은 이전 답변에서 다룬 내용에 대한 요약입니다.

내 목표는 이제 설명하여 우리의 현재 이해를 보완하는 방법 플라스크와 WERKZEUG들이 이러한 상황에 맞는 지역 주민과 함께해야합니까 것. 논리를 더 잘 이해하기 위해 코드를 단순화했지만이 정보를 얻으면 실제 소스 ( werkzeug.localflask.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_appsession생성됩니다. 우리가 확립 한대로 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, 그리고 sessionA를 직접 해결하는 객체 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:

  1. request_context환경별로 생성 (init map_adapter, match path)
  2. 이 요청을 입력하거나 푸시하십시오.
    1. 이전 지우기 request_context
    2. app_context누락되어 응용 프로그램 컨텍스트 스택으로 푸시되면 작성
    3. 이 요청은 컨텍스트 스택을 요청하도록 푸시되었습니다.
    4. 누락 된 경우 초기화 세션
  3. 파견 요청
  4. 요청을 지우고 스택에서 팝

답변

예를 들어, 사용자 컨텍스트를 설정한다고 가정하십시오 (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 ()와 동일합니다.

  1. LocalProxy는 로컬 오브젝트를 랩핑하도록 조작을 위임 하거나 대상 오브젝트를 리턴하는 함수를 제공 할 수 있습니다. 위 예제에서 get_user 함수는 현재 사용자 객체를 LocalProxy에 제공하고 usercontext.userid로 현재 사용자의 사용자 ID를 요청할 때 LocalProxy의 __getattr__ 함수는 먼저 get_user를 호출하여 User 객체 (user)를 가져온 다음 getattr (user, “userid”)를 호출합니다. User (현재 스레드 또는 Greenlet)에서 userid를 설정하려면 다음을 수행하십시오. usercontext.userid = “user_123”

답변