Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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'))
like image 554
user2499707 Avatar asked Jun 20 '13 06:06

user2499707


4 Answers

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.

like image 159
Martin Maillard Avatar answered Nov 02 '22 17:11

Martin Maillard


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
like image 25
Inego Avatar answered Nov 02 '22 15:11

Inego


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.

like image 45
rajpy Avatar answered Nov 02 '22 16:11

rajpy


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.

like image 36
chucksmash Avatar answered Nov 02 '22 16:11

chucksmash