Question

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'))
Was it helpful?

Solution 4

From the code, I can see that you want to allow user to download 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()

Now, run python aboveprogram.py

Go to http://localhost:5000

Browser prompts to download PDF.

OTHER TIPS

Martin's answer gives a good explanation of why this error occurs.

The accepted answer fixes the problem posed but it's certainly not the only way. In my case I had something more like:

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))

But when get_thing_from_datastore_render_and_cache_it was run in the background thread outside the Flask request cycle I was getting the error shown above because that thread did not have access to a request context.

The error occurs because Flask offers a developer shortcut to allow accessing request variables in the template automagically - put another way, it is caused by the decisions Flask made about how to wrap Jinja2's functionality, not Jinja2 itself. My approach to solving this was just to use Jinja2's rendering directly:

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)

That function assumes that your Flask app has the traditional templates subfolder. Specifically, the project structure here would be

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

If you have a subdirectory structure under templates/, you just pass the relative path from the root of the templates folder the same as you would when using Flask's render_template.

Flask does a whole lot of 'magic' so you don't have to worry about routing or parsing requests. When a Flask application receives a request, it creates a 'context' object before delegating the logic to your view function.

In your code, you are calling render_template directly without going through Flask, so the context is not created. render_template tries to get to your application (app) via this context (ctx), which is None, thus the error:

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

Now this is not the only thing that is wrong with your code. View functions (registered with the decorator @app.route(...)) are not meant to be called directly. @rajpy's answer gives you a good example of how they should be used.

I had the same issue when trying to render templates from Celery tasks.

What turned out to be the simplest solution was to manually push the required context:

with app.app_context():
    # Code calling render_template goes here

My problem (in celery task) was solved by rendering the template in an app context.

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

For explanation go HERE

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top