2013-06-20 52 views
6

下面的代碼提供了錯誤:AttributeError的:「NoneType」對象有沒有屬性「應用」

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' 

代碼:

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

回答

3

從代碼中,我可以看到你希望允許用戶下載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。 希望它有幫助..

10

Flask做了很多'魔術',所以你不必擔心路由或解析請求。當Flask應用程序收到請求時,它會在將邏輯委託給您的視圖函數之前創建一個「上下文」對象。

在您的代碼中,您直接呼叫render_template而不經過Flask,因此不會創建上下文。 render_template嘗試去通過這種情況下您的應用程序(app)(ctx),這是None,從而錯誤:

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

現在,這是不是就是你的代碼錯誤的嘛。查看功能(使用裝飾器@app.route(...)註冊)不能直接調用。 @ rajpy的回答給你一個很好的例子,說明它們應該如何使用。

10

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關於如何包裝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) 

該函數假定你的應用程序瓶具有傳統的模板子文件夾。具體而言,這裏的項目結構將

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

如果你有templates/下的子目錄結構,你只要從根本上通過的相對路徑的模板中使用瓶的render_template當文件夾中的相同,你會。

+0

真的很好的解釋。我沒有意識到這是因爲我在燒瓶外面跑步。 –

+0

我可以問一下,你怎麼知道包的名字? –

相關問題