2013-06-20 33 views
6

Il codice qui sotto dà errore:AttributeError: oggetto 'NoneType' non ha alcun attributo 'app'

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' 

Codice:

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

risposta

3

Dal codice, vedo che si desidera consentire all'utente per scaricare il 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() 

Ora, eseguire python aboveprogram.py

Vai http://localhost:5000

browser richiede per scaricare il PDF. Spero che sia d'aiuto ..

10

Flask fa un sacco di "magia" in modo da non doversi preoccupare di instradare o analizzare le richieste. Quando un'applicazione Flask riceve una richiesta, crea un oggetto "contesto" prima di delegare la logica alla funzione di visualizzazione.

Nel codice, si chiama render_template direttamente senza passare attraverso Flask, quindi il contesto non viene creato. render_template cerca di ottenere alla vostra applicazione (app) tramite questo contesto (ctx), che è None, quindi l'errore:

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

Ora, questo non è l'unica cosa che è sbagliato con il codice. Le funzioni di visualizzazione (registrate con il decoratore @app.route(...)) non sono destinate a essere chiamate direttamente. @ rajpy's answer ti dà un buon esempio di come dovrebbero essere usati.

10

La risposta di Martin fornisce una buona spiegazione di perché questo errore si verifica.

La risposta accettata risolve il problema, ma non è certamente l'unico modo. Nel mio caso ho avuto qualcosa di più simile:

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

Ma quando get_thing_from_datastore_render_and_cache_it è stato eseguito in thread in background al di fuori del ciclo di richiesta Flask stavo ottenendo l'errore indicato sopra, perché quel filo non ha avuto accesso a un contesto richiesta.

L'errore si verifica perché Flask offre una scorciatoia sviluppatore per consentire l'accesso a variabili di richiesta nel modello automagically - in altre parole, essa è causata da decisioni Flask fatto su come avvolgere la funzionalità di Jinja2, non Jinja2 sé. Il mio approccio alla soluzione di questo era solo per usare Jinja2 di rendere direttamente:

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) 

Tale funzione presuppone che il Flask applicazione ha il template tradizionale sottocartella. In particolare, la struttura del progetto qui sarebbe

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

Se si dispone di una struttura di sottodirectory sotto templates/, basta passare il percorso relativo dalla radice dei modelli cartella lo stesso come si farebbe quando si utilizza Flask di render_template.

+0

Davvero un'ottima spiegazione. Non mi rendevo conto che era perché stavo correndo in un compito fuori dalla fiaschetta. –

+0

Posso chiederti, come fai a sapere il nome del pacchetto? –

Problemi correlati