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'))
From the code, I can see that you want to allow user to download pdf.
Now, run
python aboveprogram.py
Go to
http://localhost:5000
Browser prompts to download PDF.
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:
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:
That function assumes that your Flask app has the traditional templates subfolder. Specifically, the project structure here would be
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'srender_template
.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:
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 isNone
, thus the error: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.My problem (in celery task) was solved by rendering the template in an app context.
For explanation go HERE