当前位置:   article > 正文

Jinja2渲染HTML模板-python发送邮件html格式正文_ctx.app.jinja_env.get_or_select_template(template_

ctx.app.jinja_env.get_or_select_template(template_name_or_list),

背景

有用过Flask的同学应该都知道,flask创建上下文之后就可以使用render_template(基于Jinja2模板引擎)去渲染HTML页面了。看这个函数的源码我们可以发现,渲染之前会创建一个ctx去获取当前环境的app变量。然后通过这个ctx去渲染传进来的context。

  1. # flask的render_template源码
  2. def render_template(
  3. template_name_or_list: t.Union[str, t.List[str]], **context: t.Any
  4. ) -> str:
  5. """Renders a template from the template folder with the given
  6. context.
  7. :param template_name_or_list: the name of the template to be
  8. rendered, or an iterable with template names
  9. the first one existing will be rendered
  10. :param context: the variables that should be available in the
  11. context of the template.
  12. """
  13. ctx = _app_ctx_stack.top
  14. ctx.app.update_template_context(context)
  15. return _render(
  16. ctx.app.jinja_env.get_or_select_template(template_name_or_list),
  17. context,
  18. ctx.app,
  19. )

需求

现在我需要在邮件正文中嵌入HTML内容,HTML里面的内容是动态变化的,因此我想到可以基于这个render_template像flask一样去渲染一个html,然后再每次动态传入context即可。

直接上代码

  1. # test.html
  2. <p style="color: red">{{test}}</p>
  3. # test.py
  4. from flask import render_template
  5. msg = render_template('test.html', test="123")

运行一下test.py就会发现出错

  1. Traceback (most recent call last):
  2. File "E:\code\flask_demo\test.py", line 3, in <module>
  3. msg = render_template('test.html', test="123")
  4. File "E:\code\flask_demo\venv\lib\site-packages\flask\templating.py", line 146, in render_template
  5. ctx.app.update_template_context(context)
  6. AttributeError: 'NoneType' object has no attribute 'app'

报错内容很容易看出来这个ctx是None,因此没有app这个属性,那为什么ctx是None呢。通过跟踪源码可以发现,此时我们并没有启动flask 应用,并不存在上下文对象,ctx是从栈里拿的,因为没有所以返回了None。

那如何解决呢,我们肯定没必要去启动一个Flask应用对吧。

解决方案

Flask把jinja2的模板渲染又经过了一层封装,因此会出现这个问题,所以我们直接跳过Flask去调用Jinja2的渲染机制不香吗。(记得安装jinja2)

代码如下:

  1. #test.py
  2. import jinja2
  3. def render_without_flask(template_name, **context):
  4. env = jinja2.Environment(
  5. loader=jinja2.PackageLoader('package', '/')
  6. )
  7. template = env.get_template(template_name)
  8. return template.render(**context)
  9. msg = render_without_flask('test.html', test="213")

运行一下test.py然后就报错了。。。

  1. Traceback (most recent call last):
  2. File "E:\code\flask_demo\test.py", line 10, in <module>
  3. msg = render_without_flask('test.html', test="213")
  4. File "E:\code\flask_demo\test.py", line 5, in render_without_flask
  5. loader=jinja2.PackageLoader('package', '/')
  6. File "E:\code\flask_demo\venv\lib\site-packages\jinja2\loaders.py", line 287, in __init__
  7. import_module(package_name)
  8. File "D:\Python3.9.6\lib\importlib\__init__.py", line 127, in import_module
  9. return _bootstrap._gcd_import(name[level:], package, level)
  10. File "<frozen importlib._bootstrap>", line 1030, in _gcd_import
  11. File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
  12. File "<frozen importlib._bootstrap>", line 984, in _find_and_load_unlocked
  13. ModuleNotFoundError: No module named 'package'

这是因为jinja2无法找到这个包。

这里要注意一下包结构,我们追踪jinja2.PackageLoader的源码,查看它是如何处理模板包路径的

这里可以看到 PackageLoader去导入包(包含html文件的那个目录/文件夹),并且这里没有指定从哪里去导入这个包(默认是从当前py文件的目录下的templates目录去找这个模板,但是我们前面PackageLoader传入了参数'package',所以它会在sys.path的所有目录下找package目录,因为都不存在所以就报错了),所以它会从sys.path里面的所有路径依次去寻找这个包,找了所有路径都没有package文件夹,因此报错。

PackageLoader的构造函数参数

        package_name指的是包含html文件的包文件夹名。

        package_path指的是包含html文件的包名。

因此如果说我们的html文件的绝对路径是这样的:E:\code\flask_demo\test.html

那我们的package_name就传入'code',package_path就传入'flask_demo',并且python解释器还要能找到'code'这个文件夹,所以我们需要把'E:'加入sys.path

即sys.path.append('E:')

当然我们也可以这样子传参:package_name='flask_demo',package_path='/',这个/是相对路径来的,表示当前目录,其实就是flask_demo,那这时解释器要怎么找到flask_demo这个文件夹呢?那当然是从E:\code里面去寻找对吧,所以我们需要用sys.path.append(r'E:\code')把这个路径加到解释器的"寻址路径"里面去。

下面我重新写一下这个渲染的函数,输入的参数改成要渲染的模板(html)的绝对路径和模板上下文变量。

  1. import os
  2. import sys
  3. import jinja2
  4. def render_without_flask(template_path, **context):
  5. dirs = os.path.split(template_path)[0]
  6. file = os.path.split(template_path)[-1]
  7. sys.path.append(os.path.split(dirs)[0])
  8. env = jinja2.Environment(
  9. loader=jinja2.PackageLoader(os.path.split(dirs)[-1], '/')
  10. )
  11. template = env.get_template(file)
  12. return template.render(**context)

接下来我们就可以调用这个函数像flask.render_template一样去渲染html模板啦!

至于发送如何发送邮件,大家自行搜索吧!!!网上有很多例子,只不过我们需要把邮件内容content换成html即可。

参考

pythonista模板_使用 jinja2 渲染 HTML 模板_weixin_39867509的博客-CSDN博客

本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号