Python Web 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
<!doctype html> <title>Hello from Flask</title> {% if name %} <h1>Hello {{ name }}!</h1> {% else %} <h1>Hello,World!</h1> {% endif %} 这个模板中 0x01 Flask.render_templatedef 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) 方法的注释很清楚,从 _request_ctx_stack = LocalStack() current_app = LocalProxy(lambda: _request_ctx_stack.top.app)
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)
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() 执行 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) 从路由原理文章的分析知道,
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 )
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) 默认情况下是从 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))
@internalcode def _load_template(self,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,globals) if self.cache is not None: self.cache[name] = template return template
0x03 BaseLoader.load@internalcode def load(self,environment,globals=None): ... # 省略部分源码 return environment.template_class.from_code(environment,code,globals,uptodate)
def render_template(template_name,**context): ... return current_app.jinja_env.get_template(template_name).render(context) 执行了 0x04 Template.renderdef 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) 这个方法接收一个 0x05 总结一下
Flask.render_template => Environment.get_template => Template.render => exec 0x06 学习资料
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- Python请求库添加了一个额外的标题“Accept-Encoding:iden
- python错误处理详解
- Python Tkinter Tk root.after延迟
- ESP8266 Micropython – 连接大学Wi-Fi(WPA2 Enterprise P
- Python实现遍历目录的方法【测试可用】
- python – Pymongo API TypeError:不可用的字典
- Django Rest Framework上的全文搜索仅支持MYSQL?
- 【10.4】线程同步--Lock、RLock
- Python:将一个characater的多次出现替换为一次,但单次出现
- Python操作MongoDB数据库PyMongo库使用方法