programing

플라스크의 컨텍스트 스택의 목적은 무엇입니까?

megabox 2023. 7. 13. 20:47
반응형

플라스크의 컨텍스트 스택의 목적은 무엇입니까?

저는 한동안 요청/응용 프로그램이 어떻게 작동하는지 또는 왜 원래대로 설계되었는지 완전히 이해하지 못한 채 요청/응용 프로그램 컨텍스트를 사용해 왔습니다.요청 또는 애플리케이션 컨텍스트와 관련하여 "스택"의 목적은 무엇입니까?이 두 개의 스택은 별개입니까, 아니면 둘 다 하나의 스택에 속합니까?요청 컨텍스트가 스택에 푸시됩니까? 아니면 스택 자체입니까?여러 컨텍스트를 서로 밀어넣거나 팝업할 수 있습니까?만약 그렇다면, 제가 왜 그렇게 하고 싶어할까요?

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

여러 앱

플라스크가 여러 개의 앱을 가질 수 있다는 것을 깨닫기 전까지는 애플리케이션 컨텍스트(및 그 목적)가 실제로 혼란스럽습니다.단일 WSGI Python 인터프리터가 여러 플라스크 응용 프로그램을 실행하도록 원하는 상황을 상상해 보십시오.우리는 청사진을 말하는 것이 아니라 완전히 다른 플라스크 응용 프로그램을 말하는 것입니다.

응용프로그램 발송 예제의 플라스크 문서 섹션과 유사하게 설정할 수 있습니다.

from werkzeug.wsgi import DispatcherMiddleware
from frontend_app import application as frontend
from backend_app import application as backend

application = DispatcherMiddleware(frontend, {
    '/backend':     backend
})

완전히 다른 두 개의 플라스크 응용프로그램이 "프론트엔드"와 "백엔드"로 작성되고 있습니다. 다른말하면로,면하▁in,Flask(...)응용 프로그램 생성자가 두 번 호출되어 플라스크 응용 프로그램의 두 인스턴스를 만들었습니다.

콘텍스트

플라스크를 사용할 때는 글로벌 변수를 사용하여 다양한 기능에 액세스하는 경우가 많습니다.예를 들어, 당신은 아마도 다음과 같은 코드를 가지고 있을 것입니다.

from flask import request

그러면 보기 중에 다음을 사용할 수 있습니다.request현재 요청의 정보에 액세스합니다. 물론. 뻔지하.request는 일반적인 전역 변수가 아닙니다. 실제로는 컨텍스트 로컬 값입니다.다시 말해서, 무대 뒤에는 "내가 전화를 걸 때"라고 말하는 어떤 마법이 있습니다.request.pathpathrequest현재 요청의 개체입니다."두 개의 서로 다른 요청은 다음에 대해 서로 다른 결과를 가집니다.request.path.

실제로 여러 스레드로 Flask를 실행하더라도 Flask는 요청 개체를 격리할 수 있을 정도로 똑똑합니다.그렇게 함으로써, 각각 다른 요청을 처리하는 두 개의 스레드가 동시에 호출하는 것이 가능해집니다.request.path각 요청에 대한 정확한 정보를 얻을 수 있습니다.

조립하기

그래서 우리는 플라스크가 동일한 인터프리터에서 여러 응용 프로그램을 처리할 수 있고 또한 플라스크가 "콘텍스트 로컬" 글로벌을 사용할 수 있는 방법 때문에 "현재" 요청이 무엇인지 결정하는 메커니즘이 있어야 한다는 것을 이미 보았습니다.request.path).

이러한 아이디어를 종합하면 플라스크는 "현재" 응용 프로그램이 무엇인지 결정할 수 있는 방법이 있어야 한다는 것도 의미가 있습니다!

다음과 유사한 코드도 있을 수 있습니다.

from flask import url_for

우처럼처럼.request를 들어, 들를어, url_for함수에는 현재 환경에 종속된 논리가 있습니다.그러나 이 경우 어떤 앱이 "현재" 앱으로 간주되는지에 따라 논리가 크게 좌우된다는 것은 분명합니다.에서 "및 "앱"/를 가질 수 "/login" " "/login"은 "/" 경로입니다.url_for('/login')보기가 프런트엔드 또는 백엔드 앱에 대한 요청을 처리하는지 여부에 따라 다른 값을 반환해야 합니다.

질문에 답하려면...

요청 또는 애플리케이션 컨텍스트와 관련하여 "스택"의 목적은 무엇입니까?

요청 컨텍스트 문서에서:

요청 컨텍스트는 내부적으로 스택으로 유지되므로 여러 번 푸시 및 팝업할 수 있습니다.이것은 내부 리디렉션과 같은 것들을 구현하는 데 매우 유용합니다.

즉, 일반적으로 이러한 "현재" 요청 또는 "현재" 응용프로그램 스택에 0개 또는 1개의 항목이 있더라도 더 많은 항목이 있을 수 있습니다.

주어진 예는 요청이 "내부 리디렉션"의 결과를 반환하도록 하는 경우입니다.사용자가 A를 요청하지만 사용자 B로 돌아가고 싶다고 가정합니다.대부분의 경우 사용자에게 리디렉션을 실행하고 사용자에게 리소스 B를 가리킵니다. 즉, 사용자가 두 번째 요청을 실행하여 B를 가져옵니다.이것을 처리하는 약간 다른 방법은 내부 리디렉션을 수행하는 것입니다. 즉, A를 처리하는 동안 플라스크는 리소스 B에 대해 자신에게 새 요청을 하고 이 두 번째 요청의 결과를 사용자의 원래 요청의 결과로 사용합니다.

이 두 개의 스택은 별개입니까, 아니면 둘 다 하나의 스택에 속합니까?

그것들은 두 의 별도 스택입니다.그러나 이것은 구현 세부사항입니다.더 중요한 것은 스택이 있다는 것이 아니라 언제든지 "현재" 애플리케이션 또는 요청(스택의 맨 위)을 받을 수 있다는 사실입니다.

요청 컨텍스트가 스택에 푸시됩니까? 아니면 스택 자체입니까?

"요청 컨텍스트"는 "요청 컨텍스트 스택"의 한 항목입니다."앱 컨텍스트" 및 "앱 컨텍스트 스택"에서도 마찬가지입니다.

여러 컨텍스트를 서로 밀어넣거나 팝업할 수 있습니까?만약 그렇다면, 제가 왜 그렇게 하고 싶어할까요?

플라스크 응용 프로그램에서는 일반적으로 이 작업을 수행하지 않습니다.원하는 위치의 한 예로 내부 리디렉션이 있습니다(위에서 설명).그러나 이 경우에도 플라스크가 새로운 요청을 처리하게 되므로 모든 푸시/팝핑이 플라스크에서 대신 처리됩니다.

그러나 스택을 직접 조작하려는 경우도 있습니다.

요청 외부에서 코드 실행 중

사람들이 가지고 있는 한 가지 일반적인 문제는 아래와 같은 코드를 사용하여 SQL 데이터베이스와 모델 정의를 설정하기 위해 Flask-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()

플라스크-SQ 경우플-SQ는LA 화학 LA 화학다대알에 대해 있습니다.app 프로그램, 용응프램그 동안, 그나실행중러.create_all()응용프로그램 컨텍스트가 없다고 불평하는 오류가 발생합니다.됩니다. 당신은 할 때 해야 하는지 않았습니다. 당신은 플라스크를 실행할 때 어떤 애플리케이션을 다루어야 하는지 결코 말하지 않았습니다.create_all방법.

당신은 왜 당신이 이것이 필요하지 않은지 궁금할 것입니다.with app.app_context()보기에서 유사한 기능을 실행할 때 호출합니다.그 이유는 플라스크가 실제 웹 요청을 처리할 때 이미 응용프로그램 컨텍스트의 관리를 처리하기 때문입니다.이 문제는 일회성 스크립트에서 모델을 사용하는 경우와 같이 이러한 보기 기능(또는 다른 콜백) 밖에서만 발생합니다.

해결 방법은 애플리케이션 컨텍스트를 직접 푸시하는 것입니다. 이 작업은 다음 작업을 통해 수행할 수 있습니다.

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

이전 답변에서는 이미 요청 중 플라스크의 배경에서 진행되는 작업에 대해 좋은 개요를 제공합니다.아직 읽지 않으셨다면 이 글을 읽기 전에 @Mark Hildreth의 답변을 추천합니다.즉, 각 http 요청에 대해 새 컨텍스트(스레드)가 생성되므로 스레드가 필요합니다.Local다과같은물허시설용는하체를과 같은 을 허용하는 request그리고.g요청별 컨텍스트를 유지하면서 스레드 전체에서 전체적으로 액세스할 수 있습니다.또한 http 요청을 처리하는 동안 Flask는 내부에서 추가 요청을 에뮬레이트할 수 있으므로 각각의 컨텍스트를 스택에 저장해야 합니다.또한 Flask를 사용하면 단일 프로세스 내에서 여러 wsgi 응용프로그램을 서로 실행할 수 있으며, 요청 중에 둘 이상의 작업을 호출할 수 있습니다(각 요청이 새 응용프로그램 컨텍스트를 생성함). 따라서 응용프로그램을 위한 컨텍스트 스택이 필요합니다.이전 답변에서 다룬 내용을 요약한 것입니다.

제 목표는 플라스크와 Werkzeug가 이러한 상황에 맞는 현지인들과 어떻게 하는지 설명함으로써 현재의 이해를 보완하는 것입니다.코드의 논리에 대한 이해를 높이기 위해 코드를 단순화했지만, 이것을 알게 되면 실제 소스에 있는 대부분의 내용을 쉽게 파악할 수 있을 것입니다(werkzeug.local그리고.flask.globals).

먼저 Werkzeug가 스레드 Local을 구현하는 방법에 대해 알아보겠습니다.

현지의

http 요청이 들어오면 단일 스레드의 컨텍스트 내에서 처리됩니다.Werkzeug는 http 요청 중에 새로운 컨텍스트를 생성하는 대안으로 일반 스레드 대신 그린렛(일종의 가벼운 "마이크로 스레드")을 사용할 수도 있습니다.그린렛이 설치되어 있지 않으면 대신 스레드를 사용하는 것으로 돌아갑니다.는 각각 ID로 할 수 " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " 을 할 수 .get_ident()기능.그 기능이 마법의 시작점입니다.request,current_app,url_for,g기타 컨텍스트 바운드 글로벌 객체.

try:
    from greenlet import get_ident
except ImportError:
    from thread import get_ident

어떤 알 수 수 .Local전체적으로 액세스할 수 있지만 해당 특성에 액세스할 때 특정 스레드의 값으로 확인됩니다.

# 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물건들.뭐가LocalProxy?

로컬 프록시

는 LocalProxy를 입니다.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" pop)에 프록시는 여전히 글로벌하게 액세스할 수 있으며 프록시에 종속됩니다.Local후속 http 요청에 대한 관심 대상을 찾습니다.

# this would happen some time near the end of request processing
release(local) # aka local.__release_local__()

생을단려면 과 같이 하십시오.LocalProxy우리가 이미 가지고 있을 때.Local는 Werkzeug를 합니다.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')

.request,g,current_app그리고.session 우리가했듯이 (단에서) 개의 "할 수 , 그 컨텍스트도 수 있습니다.우리가 확립한 바와 같이, 플라스크는 (단 하나의 실제 http 요청에서) 여러 개의 "가짜" 요청을 생성할 수 있으며, 그 과정에서 여러 개의 애플리케이션 컨텍스트도 푸시할 수 있습니다.이는 일반적인 사용 사례는 아니지만 프레임워크의 기능입니다.이러한 "동시적" 요청과 앱은 여전히 언제든지 "초점"이 있는 하나만 실행하도록 제한되므로, 각 컨텍스트에 스택을 사용하는 것이 타당합니다.새 요청이 생성되거나 애플리케이션 중 하나가 호출될 때마다 각 스택의 맨 위에 컨텍스트가 푸시됩니다.는 플스크용도를 합니다.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 에서언바같이와a입니다.LocalStack로컬에 저장된 스택이지, 스택에 저장된 로컬의 묶음이 아닙니다.이는 스택이 전체적으로 액세스할 수 있지만 각 스레드에서 서로 다른 스택임을 의미합니다.

플라스크는 그것을 가지고 있지 않습니다.request,current_app,g,그리고.session는 "" " " " 로 확인LocalStack그것은 오히려 사용합니다.LocalProxy룩업 함수를 래핑하는 객체(대신)Localobject에서 기본 를 찾을 수 .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)

이 모든 것은 애플리케이션 시작 시 선언되지만, 요청 컨텍스트 또는 애플리케이션 컨텍스트가 각각의 스택에 푸시될 때까지 실제로 아무것도 해결되지 않습니다.

여러분이 어떤 맥락이 그 후에 ) , ▁in▁if,▁look면▁is다▁a궁),하금▁how▁context▁(스▁out▁stack▁you,flask.app.Flask.wsgi_app()앱의 웹 할 때 이며, wsgi 앱즉의이다("wsgi", "http 경환것호을전")의 . 그리고 다음의 생성을 따릅니다.RequestContext는 후속 반대내내▁its까지 .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따라서 보기 기능 중 하나는 다음과 같습니다.

  • 액세스할 수 있는 " ▁the▁from다"에서 시작합니다.LocalProxyrequest.
  • 을 위해 검색 를 함프고하기를는관본개 (심체중개체인다니찾습)검색시라고 합니다._find_request()()입니다self.local).
  • 는 그함는다쿼니다합리를 .LocalStack_request_ctx_stack스택의 맨 위 컨텍스트에 사용할 수 있습니다.
  • 위의 , 상단컨를위해찾기트텍스▁to,,▁the위,LocalStack의 개가먼저내쿼부리합다니를체에 대해 .Local속성)self.local) 의stack이전에 저장된 속성입니다.
  • stack그것은 최고의 맥락을 얻습니다.
  • 그리고.top.request따라서 기본적인 관심 대상으로 해결됩니다.
  • 우리는 그 물체로부터 얻습니다.path속성

그래서 우리는 어떻게Local,LocalProxy,그리고.LocalStack일, 이제 검색에 대한 의미와 뉘앙스를 잠시 생각해 보십시오.path 사람보내기:

  • a request전역적으로 액세스할 수 있는 단순한 개체입니다.
  • a request로컬일 수 있는 개체입니다.
  • a request로컬의 특성으로 저장된 개체입니다.
  • a request로컬에 저장된 개체에 대한 프록시인 개체입니다.
  • a request스택에 저장된 개체, 즉 로컬에 저장됩니다.
  • a request로컬에 저장된 스택의 개체에 대한 프록시인 개체입니다.<- 플라스크가 하는 일입니다.

약간의 추가 @마크 힐드레스의 대답.

모양{thread.get_ident(): []}서, 디에어[]"stack"은 "stack"만 .append(push),pop그리고.[-1](__getitem__(-1) 또는 스레드에 합니다.따라서 컨텍스트 스택은 스레드 또는 그린릿 스레드에 대한 실제 데이터를 유지합니다.

current_app,g,request,session은 등은입니다.LocalProxy 특수 메소드를 입니다.__getattr__,__getitem__,__call__,__eq__ context top에서 되는 값 (및컨텍트반등값환상의단스택스값▁from(▁value▁and등반▁context▁(환▁return▁stack▁top▁etc의▁and)[-1] 이름 )에 current_app,request예를 들어). LocalProxy이 개체를 한 번 가져오면 실제 개체를 놓치지 않습니다.그러니 그냥 수입하는 게 낫습니다.request사용자가 코드에 있는 곳이면 어디든 대신 요청 인수를 함수와 메서드로 보내는 놀이를 합니다.당신은 그것으로 쉽게 확장자를 작성할 수 있지만, 경솔한 사용은 코드를 더 이해하기 어렵게 만들 수 있다는 것을 잊지 마세요.

https://github.com/mitsuhiko/werkzeug/blob/master/werkzeug/local.py 을 이해하는 데 시간을 할애합니다.

그렇다면 두 스택 모두 어떻게 채워집니까?Flask:

  1. 를 만들다request_context 환별경in(in))map_adapter path를 합니다.
  2. 이 요청 입력 또는 푸시:
    1. request_context
    2. 를 만들다app_context 컨텍스트 스택을 에는 다음과 같이 .
    3. 이 요청을 요청 컨텍스트 스택에 푸시했습니다.
    4. 세션에서 누락된 경우
  3. 발송요청서
  4. 요청 지우기 및 스택에서 팝업

예를 들어, 로컬 및 로컬 프록시의 플라스크 구조를 사용하여 사용자 컨텍스트를 설정하려고 합니다.

하나의 사용자 클래스 정의:

class User(object):
    def __init__(self):
        self.userid = None

현재 스레드 또는 그린릿 내부의 사용자 개체를 검색하는 함수 정의

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

이제 로컬 프록시 정의

usercontext = LocalProxy(partial(get_user, Local()))

현재 스레드 usercontext.userid에서 사용자의 사용자 ID를 가져옵니다.

설명:

  1. 로컬에는 ID와 개체에 대한 딕트가 있습니다.ID가 스레드 ID 또는 그린릿 ID입니다.예에서는, 이예서는에,는,_local.user = User() ._local.___storage__[current thread's id] ["user"] = User()

  2. LocalProxy는 작업을 랩핑된 Local 객체에 위임하거나 대상 객체를 반환하는 함수를 제공할 수 있습니다.위의 예에서get_user를 함는현사제다니공에 합니다.LocalProxy▁for▁▁you때▁and▁user▁when.userid타고usercontext.userid,LocalProxy__getattr__가 먼저 을 합니다.get_user갖기 위해User객체를 호출한 체사(사용자)호및을 합니다.getattr(user,"userid")설정하기useridUser 다음을 수행합니다.usercontext.userid = "user_123"

언급URL : https://stackoverflow.com/questions/20036520/what-is-the-purpose-of-flasks-context-stacks

반응형