Pythonのウェブフラスコソースの解釈(3) - テンプレートのレンダリング処理


プログラム猿思慮深く、生涯学習の専門家は、スタートアップのチームで、現在どのチームリーダーである、技術スタックは、AndroidやPython、Java、および移動を必要とする、これが主な技術が私たちのチームをスタックです。
GitHubの:HTTPS://github.com/hylinux1024
マイクロチャネルパブリック番号:生涯現像剤(angrycode)

前にFlask起動処理及びルーティング原理ソース通勤されています。今日は見とりテンプレートレンダリングプロセスを。

0x00のテンプレートを使用して

からの公式文書をレンダリングするためのテンプレートを使用した例ではまず見て

from flask import render_template

@app.route('/hello/')
@app.route('/hello/<name>')
def hello(name=None):
    return render_template('hello.html', name=name)

プロジェクトディレクトリの下に必要templatesディレクトリを作成し、hello.htmlファイルを

/templates
    /hello.html

hello.htmlコンテンツ

<!doctype html>
<title>Hello from Flask</title>
{% if name %}
  <h1>Hello {{ name }}!</h1>
{% else %}
  <h1>Hello, World!</h1>
{% endif %}

このテンプレートは、name呼び出しによってパラメータでありrender_template、パラメータの方法に従って達成することができhtml、テンプレートファイルをレンダリングします。

0x01のFlask.render_template

def render_template(template_name, **context):
    """Renders a template from the template folder with the given
    context.

    :param template_name: the name of the template to be rendered
    :param context: the variables that should be available in the
                    context of the template.
    """
    current_app.update_template_context(context)
    return current_app.jinja_env.get_template(template_name).render(context)

アプローチから、明らかである注意事項templatesにフォルダの名前を見つけるtemplate_nameレンダリングするためのファイル。これはcurrent_app次の文で初期化されます

_request_ctx_stack = LocalStack()
current_app = LocalProxy(lambda: _request_ctx_stack.top.app)

LocalStackこれは、あるクラスのスタックの実現そして、_request_ctx_stack中にFlask.request_context()例の方法では、現在のコンテキストpushのスタックの内部に

def request_context(self, environ):
    """Creates a request context from the given environment and binds
    it to the current context.  This must be used in combination with
    the `with` statement because the request is only bound to the
    current context for the duration of the `with` block.

    Example usage::

        with app.request_context(environ):
            do_something_with(request)

    :params environ: a WSGI environment
    """
    return _RequestContext(self, environ)

_RequestContextクラスは、プロトコルコンテキストマネージャを実装し、それができるwithの文を使用します

class _RequestContext(object):
    """The request context contains all request relevant information.  It is
    created at the beginning of the request and pushed to the
    `_request_ctx_stack` and removed at the end of it.  It will create the
    URL adapter and request object for the WSGI environment provided.
    """

    def __init__(self, app, environ):
        self.app = app
        self.url_adapter = app.url_map.bind_to_environ(environ)
        self.request = app.request_class(environ)
        self.session = app.open_session(self.request)
        self.g = _RequestGlobals()
        self.flashes = None

    def __enter__(self):
        _request_ctx_stack.push(self)

    def __exit__(self, exc_type, exc_value, tb):
        # do not pop the request stack if we are in debug mode and an
        # exception happened.  This will allow the debugger to still
        # access the request object in the interactive shell.
        if tb is None or not self.app.debug:
            _request_ctx_stack.pop()

実行するには__enter__()、操作push、終了をwith文の実装にpop操作。
バックrequest_context()である方法、wsgi_app()で呼ばれるように

def wsgi_app(self, environ, start_response):
    """The actual WSGI application.  This is not implemented in
    `__call__` so that middlewares can be applied:

        app.wsgi_app = MyMiddleware(app.wsgi_app)

    :param environ: a WSGI environment
    :param start_response: a callable accepting a status code,
                           a list of headers and an optional
                           exception context to start the response
    """
    with self.request_context(environ):
        rv = self.preprocess_request()
        if rv is None:
            rv = self.dispatch_request()
        response = self.make_response(rv)
        response = self.process_response(response)
        return response(environ, start_response)

以下からの原理は、ルーティングの分析記事を知られており、wsgi_app()サーバはクライアントの要求を受信したときに実行されます。
要求が来たときしたがって、電流がされるFlaskインスタンスのコンテキスト・インスタンスを要求されたインスタンスをスタックに退避され_request_ctx_stack、内部のスタックから現在のコンテキスト・インスタンスの要求をポップするために、要求処理。

LocalProxyプロキシクラスは、コンストラクタが渡され、あるlambda式を:lambda: _request_ctx_stack.top.app
この操作は、現在のコンテキスト・インスタンスを介して置かれLocalProxy、即ち、カプセル化されているcurrent_app現在のFlaskプロキシ・インスタンスのコンテキスト。
ときにcurrent_app.jinja_env文が実際にアクセスするFlaskインスタンスの属性をjinja_env、このプロパティはであるFlask初期化するコンストラクタ。

class Flask(object):
    ...
    #: 源码太长了省略
    #: options that are passed directly to the Jinja2 environment
    jinja_options = dict(
        autoescape=True,
        extensions=['jinja2.ext.autoescape', 'jinja2.ext.with_']
    )

    def __init__(self, package_name):
        ...
        #: 源码太长省略部分源码
        #: the Jinja2 environment.  It is created from the
        #: :attr:`jinja_options` and the loader that is returned
        #: by the :meth:`create_jinja_loader` function.
        self.jinja_env = Environment(loader=self.create_jinja_loader(),
                                     **self.jinja_options)
        self.jinja_env.globals.update(
            url_for=url_for,
            get_flashed_messages=get_flashed_messages
        )

jinja_envこれは、あるEnvironment例。これは、jinjaテンプレートエンジンは、クラスの提供Flaskテンプレートフレームのレンダリングが通過でjinja達成します。
Environment必要性はloader、以下の方法により得られます

def create_jinja_loader(self):
    """Creates the Jinja loader.  By default just a package loader for
    the configured package is returned that looks up templates in the
    `templates` folder.  To add other loaders it's possible to
    override this method.
    """
    if pkg_resources is None:
        return FileSystemLoader(os.path.join(self.root_path, 'templates'))
    return PackageLoader(self.package_name)

デフォルトでは、からtemplatesディレクトリ構築するFileSystemLoaderインスタンスを、このクラスの役割は、ファイルシステムからテンプレートファイルをロードすることです。

0x02のEnvironment.get_template

@internalcode
def get_template(self, name, parent=None, globals=None):
    """Load a template from the loader.  If a loader is configured this
    method ask the loader for the template and returns a :class:`Template`.
    If the `parent` parameter is not `None`, :meth:`join_path` is called
    to get the real template name before loading.

    The `globals` parameter can be used to provide template wide globals.
    These variables are available in the context at render time.

    If the template does not exist a :exc:`TemplateNotFound` exception is
    raised.

    .. versionchanged:: 2.4
       If `name` is a :class:`Template` object it is returned from the
       function unchanged.
    """
    if isinstance(name, Template):
        return name
    if parent is not None:
        name = self.join_path(name, parent)
    return self._load_template(name, self.make_globals(globals))

get_template()内部メソッド呼び出し_load_template()方法を

@internalcode
def _load_template(self, name, globals):
    if self.loader is None:
        raise TypeError('no loader for this environment specified')
    if self.cache is not None:
        template = self.cache.get(name)
        if template is not None and (not self.auto_reload or \
                                     template.is_up_to_date):
            return template
    template = self.loader.load(self, name, globals)
    if self.cache is not None:
        self.cache[name] = template
    return template

_load_template()方法キャッシュは、キャッシュの使用に利用可能である場合、キャッシュが存在するかどうかを最初にチェックし、キャッシュが使用するために利用可能でないloaderテンプレートを読み込むためには、これはloader以前に記載されているFileSystemLoader(デフォルトで)インスタンス。

0x03のBaseLoader.load

@internalcode
def load(self, environment, name, globals=None):
    ...
    # 省略部分源码
    return environment.template_class.from_code(environment, code, globals, uptodate)

BaseLoaderこれは、あるFileSystemLoader基底クラス。このload方法は、このような負荷の論理として、テンプレートの編集を可能にします。最後に、使用するenvironment.template_class.from_code()方法を。どちらtemplate_classTemplateコンパイルされたテンプレートオブジェクトを表すクラス。
from_codeTemplate作成するために使用することができる静的メソッドの種類Templateのインスタンスを。場合loadメソッド戻り、そこに得られたTemplateターゲットを。
そして最後に、バックrender_template方法

def render_template(template_name, **context):
    ...
    return current_app.jinja_env.get_template(template_name).render(context)

実装Templateオブジェクトrender()のメソッド。

0x04のTemplate.render

def render(self, *args, **kwargs):
    """This function accepts either a dict or some keyword arguments which
    will then be the context the template is evaluated in.  The return
    value will be the rendered template.

    :param context: the function accepts the same arguments as the
                    :class:`dict` constructor.
    :return: the rendered template as string
    """
    ns = self.default_context.copy()
    if len(args) == 1 and isinstance(args[0], utils.MultiDict):
        ns.update(args[0].to_dict(flat=True))
    else:
        ns.update(dict(*args))
    if kwargs:
        ns.update(kwargs)
    context = Context(ns, self.charset, self.errors)
    exec self.code in context.runtime, context
    return context.get_value(self.unicode_mode)

この方法は、受信したdictテンプレートにパラメータを渡すために使用されるタイプのパラメータを、。方法コアが実行するexec機能。組み込み関数は、動的実行できるコード。execPythonPython

0x05のまとめ

Flask使用するJinjaテンプレートエンジンとして。実行パス

Flask.render_template => Environment.get_template => Template.render => exec

0x06の学習教材

  • https://palletsprojects.com/p/flask/
  • http://jinja.pocoo.org/docs/2.10/

おすすめ

転載: www.cnblogs.com/angrycode/p/11449210.html