11

The below code gives error:

Traceback (most recent call last):
  File "pdf.py", line 14, in <module>
    create_pdf(render_template('templates.htm'))
  File "/usr/local/lib/python2.7/dist-packages/flask/templating.py", line 123, in render_template
    ctx.app.update_template_context(context)
AttributeError: 'NoneType' object has no attribute 'app'

Code:

from xhtml2pdf import pisa
from StringIO import StringIO
from flask import render_template,Flask

app=Flask(__name__)
app.debug=True

@app.route("/")
def create_pdf(pdf_data):
        filename= "file.pdf"
        pdf=pisa.CreatePDF( StringIO(pdf_data),file(filename, "wb"))

if __name__ == "__main__":
        create_pdf(render_template('templates.htm'))
4

5 回答 5

17

Martin 的回答很好地解释了为什么会发生此错误。

公认的答案解决了所提出的问题,但它肯定不是唯一的方法。就我而言,我有更多类似的东西:

import threading

from flask import Flask, render_template

app = Flask("myapp")

app.route('/')
def get_thing(thing_id):
    thing = cache.get(thing_id)
    if thing is None:
        # Handle cache miss...
    elif is_old(thing):
        # We'll serve the stale content but let's
        # update the cache in a background thread
        t = threading.Thread(
            target=get_thing_from_datastore_render_and_cache_it,
            args=(thing_id,)
        )
        t.start()
    return thing

def get_thing_from_datastore_render_and_cache_it(thing_id):
    thing = datastore.get(thing_id)
    cache.set(render_template(thing))

但是当get_thing_from_datastore_render_and_cache_it在 Flask 请求周期之外的后台线程中运行时,我得到了上面显示的错误,因为该线程无权访问请求上下文。

发生错误是因为 Flask 提供了一个开发人员快捷方式来允许自动访问模板中的请求变量 - 换句话说,这是由 Flask 关于如何包装 Jinja2 的功能而不是 Jinja2 本身所做的决定引起的。我解决这个问题的方法就是直接使用 Jinja2 的渲染:

import jinja2

def render_without_request(template_name, **template_vars):
    """
    Usage is the same as flask.render_template:

    render_without_request('my_template.html', var1='foo', var2='bar')
    """
    env = jinja2.Environment(
        loader=jinja2.PackageLoader('name.ofmy.package','templates')
    )
    template = env.get_template(template_name)
    return template.render(**template_vars)

该功能假定您的 Flask 应用程序具有传统的模板子文件夹。具体来说,这里的项目结构将是

.
└── name/
    ├── ofmy/
    |   ├── package/
    |   |   ├── __init__.py <--- Where your Flask application object is defined
    |   |   └── templates/
    |   |       └── my_template.html
    |   └── __init__.py
    └── __init__.py

如果你有一个子目录结构templates/,你只需传递模板文件夹根目录的相对路径,就像使用 Flask 的render_template.

于 2015-02-05T22:49:42.257 回答
13

Flask 做了很多“魔术”,因此您不必担心路由或解析请求。当 Flask 应用程序收到请求时,它会在将逻辑委托给您的视图函数之前创建一个“上下文”对象。

在您的代码中,您render_template无需通过 Flask 直接调用,因此不会创建上下文。render_template尝试app通过此上下文 ( ) 访问您的应用程序 ( ctx),即None,因此出现错误:

AttributeError: 'NoneType' object has no attribute 'app'

现在,这不是您的代码唯一有问题的地方。视图函数(使用装饰器注册@app.route(...))并不意味着直接调用。@rajpy 的回答为您提供了如何使用它们的一个很好的例子。

于 2013-06-20T08:48:01.790 回答
12

我在尝试从 Celery 任务中渲染模板时遇到了同样的问题。

结果证明最简单的解决方案是手动推送所需的上下文:

with app.app_context():
    # Code calling render_template goes here
于 2018-06-19T11:25:22.583 回答
6

从代码中,我可以看到您希望允许用户下载 pdf。

from xhtml2pdf import pisa
from StringIO import StringIO
from flask import render_template,Flask, Response

app=Flask(__name__)
app.debug=True

@app.route("/")
def create_pdf(pdf_data):
        filename= "file.pdf"
        pdf=pisa.CreatePDF( StringIO(pdf_data),file(filename, "wb"))
        return Response(pdf, mimetype='application/octet-stream',
                        headers={"Content-Disposition": "attachment;filename=%s" % filename})

if __name__ == "__main__":
        app.run()

现在,运行python aboveprogram.py

http://localhost:5000

浏览器提示下载 PDF。

于 2013-06-20T07:04:18.687 回答
0

我的问题(在 celery 任务中)通过在应用程序上下文中呈现模板来解决。

with app.app_context(), app.test_request_context():
    template = render_template('home')

有关解释,请转到此处

于 2020-03-14T17:30:36.293 回答