AttributeError: объект' NoneType 'не имеет атрибута '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'
код:
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'))
4 ответов
из кода я вижу, что вы хотите разрешить пользователю загружать 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. Надеюсь, это поможет..
ответ Мартина дает хорошее объяснение почему эта ошибка возникает.
принято отвечать устраняет проблему, но это конечно не единственный способ. В моем случае у меня было что-то вроде:
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
был запущен в фоновом потоке вне цикла запроса колбы я получал ошибку, показанную выше, потому что этот поток не имел доступа к контексту запроса.
ошибка возникает, потому что колба предлагает ярлык разработчика для автоматического доступа к переменным запроса в шаблоне-иными словами, это вызвано решениями колбы, сделанными о том, как обернуть функциональность 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)
эта функция предполагает, что ваше приложение Flask имеет традиционную подпапку шаблонов. В частности, структура проекта здесь будет
.
└── name/
├── ofmy/
| ├── package/
| | ├── __init__.py <--- Where your Flask application object is defined
| | └── templates/
| | └── my_template.html
| └── __init__.py
└── __init__.py
если у вас есть поддиректории структура под templates/
, вы просто передаете относительный путь из корня папки шаблонов так же, как и при использованииrender_template
.
Flask делает много "магии", поэтому вам не нужно беспокоиться о маршрутизации или разборе запросов. Когда приложение Flask получает запрос, оно создает объект "контекст" перед делегированием логики вашей функции представления.
в вашем коде вы вызываете render_template
напрямую, не проходя через колбу, поэтому контекст не создается. render_template
пытается добраться до вашего приложения (app
) через этот контекст (ctx
), которая составляет None
, таким образом, ошибка:
AttributeError: 'NoneType' object has no attribute 'app'
теперь это не единственное, что не так с вашим кодом. Просмотр функций (зарегистрирован у декоратора @app.route(...)
) не предназначены для прямого вызова. ответ @rajpy дает вам хороший пример того, как их следует использовать.
У меня была такая же проблема при попытке рендеринга шаблонов из задач сельдерея.
самым простым решением оказалось вручную толкать нужном контексте:
with app.app_context():
# Code calling render_template goes here