Using url_for
in a macro defined in another file in a template raises a KeyError for 'request'.
Considering the following tree
.
|-- app.py
|-- static
| |-- css
| | `-- style.css
| |-- img
| | `-- logo.png
| `-- js
| |-- script.js
`-- templates
|-- base.html
|-- index.html
`-- macros
`-- header.html
And this app.py
from fastapi import FastAPI, Request, UploadFile, HTTPException
from fastapi.responses import HTMLResponse, FileResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
app = FastAPI()
app.mount("/static", StaticFiles(directory="static"), name="static")
app.mount("/static/css", StaticFiles(directory="static/css"), name="css")
app.mount("/static/img", StaticFiles(directory="static/img"), name="img")
app.mount("/static/js", StaticFiles(directory="static/js"), name="js")
app.mount("/mails", StaticFiles(directory="mails"), name="mails")
templates = Jinja2Templates(directory="templates")
@app.get("/", response_class=HTMLResponse)
async def index(request: Request):
return templates.TemplateResponse("index.html", {"request": request})
if in base.html i define the macro in the same file it works perfectly.
{% macro header(request) %}
<header class="header">
<div class="header__logo">
{{ request.url }}
<img class="header__logo-image"
alt="Email Sharer"
src="{{ url_for('img', path='logo.png') }}">
</img>
</div>
</header>
{% endmacro %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Item Details</title>
</head>
<body>
{{ header(request) }}
{% block content %}
{% endblock content %}
</body>
</html>
The problem arises if i try to define the header macro in another file
{# templates/base.html #}
{% import 'macros/header.html' as header %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Item Details</title>
<link href="{{ url_for('css', path='style.css') }}" rel="stylesheet">
</head>
<body>
{{ header.header(request) }}
{% block content %}
{% endblock content %}
</body>
</html>
{# templates/macros/header.html #}
{% macro header(request) %}
<header class="header">
<div class="header__logo">
{{ request.url }}
<img class="header__logo-image"
alt="Email Sharer"
src="{{ url_for('img', path='logo.png') }}">
</img>
</div>
</header>
{% endmacro %}
When i do that, i fastAPI raises this error in the url_for
INFO: 127.0.0.1:57681 - "GET / HTTP/1.1" 500 Internal Server Error
ERROR: Exception in ASGI application
Traceback (most recent call last):
File "C:\Users\krappr\dev\email-sharer\.venv\Lib\site-packages\uvicorn\protocols\http\httptools_impl.py", line 426, in run_asgi
result = await app( # type: ignore[func-returns-value]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\krappr\dev\email-sharer\.venv\Lib\site-packages\uvicorn\middleware\proxy_headers.py", line 84, in __call__
return await self.app(scope, receive, send)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\krappr\dev\email-sharer\.venv\Lib\site-packages\fastapi\applications.py", line 292, in __call__
await super().__call__(scope, receive, send)
File "C:\Users\krappr\dev\email-sharer\.venv\Lib\site-packages\starlette\applications.py", line 122, in __call__
await self.middleware_stack(scope, receive, send)
File "C:\Users\krappr\dev\email-sharer\.venv\Lib\site-packages\starlette\middleware\errors.py", line 184, in __call__
raise exc
File "C:\Users\krappr\dev\email-sharer\.venv\Lib\site-packages\starlette\middleware\errors.py", line 162, in __call__
await self.app(scope, receive, _send)
File "C:\Users\krappr\dev\email-sharer\.venv\Lib\site-packages\starlette\middleware\exceptions.py", line 79, in __call__
raise exc
File "C:\Users\krappr\dev\email-sharer\.venv\Lib\site-packages\starlette\middleware\exceptions.py", line 68, in __call__
await self.app(scope, receive, sender)
File "C:\Users\krappr\dev\email-sharer\.venv\Lib\site-packages\fastapi\middleware\asyncexitstack.py", line 20, in __call__
raise e
File "C:\Users\krappr\dev\email-sharer\.venv\Lib\site-packages\fastapi\middleware\asyncexitstack.py", line 17, in __call__
await self.app(scope, receive, send)
File "C:\Users\krappr\dev\email-sharer\.venv\Lib\site-packages\starlette\routing.py", line 718, in __call__
await route.handle(scope, receive, send)
File "C:\Users\krappr\dev\email-sharer\.venv\Lib\site-packages\starlette\routing.py", line 276, in handle
await self.app(scope, receive, send)
File "C:\Users\krappr\dev\email-sharer\.venv\Lib\site-packages\starlette\routing.py", line 66, in app
response = await func(request)
^^^^^^^^^^^^^^^^^^^
File "C:\Users\krappr\dev\email-sharer\.venv\Lib\site-packages\fastapi\routing.py", line 273, in app
raw_response = await run_endpoint_function(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\krappr\dev\email-sharer\.venv\Lib\site-packages\fastapi\routing.py", line 190, in run_endpoint_function
return await dependant.call(**values)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\krappr\dev\email-sharer\app\app.py", line 86, in index
return templates.TemplateResponse("index.html", {"request": request})
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\krappr\dev\email-sharer\.venv\Lib\site-packages\starlette\templating.py", line 113, in TemplateResponse
return _TemplateResponse(
^^^^^^^^^^^^^^^^^^
File "C:\Users\krappr\dev\email-sharer\.venv\Lib\site-packages\starlette\templating.py", line 39, in __init__
content = template.render(context)
^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\krappr\dev\email-sharer\.venv\Lib\site-packages\jinja2\environment.py", line 1301, in render
self.environment.handle_exception()
File "C:\Users\krappr\dev\email-sharer\.venv\Lib\site-packages\jinja2\environment.py", line 936, in handle_exception
raise rewrite_traceback_stack(source=source)
File "templates\index.html", line 1, in top-level template code
{% extends "base.html" %}
File "templates\base.html", line 12, in top-level template code
{{ header.header(request) }}
^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\krappr\dev\email-sharer\.venv\Lib\site-packages\jinja2\runtime.py", line 777, in _invoke
rv = self._func(*arguments)
^^^^^^^^^^^^^^^^^^^^^^
File "templates\macros\header.html", line 7, in template
src="{{ url_for('img', path='logo.png') }}">
^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\krappr\dev\email-sharer\.venv\Lib\site-packages\starlette\templating.py", line 82, in url_for
request = context["request"]
~~~~~~~^^^^^^^^^^^
File "C:\Users\krappr\dev\email-sharer\.venv\Lib\site-packages\jinja2\runtime.py", line 331, in __getitem__
raise KeyError(key)
KeyError: 'request'
You should import with context
, see https://jinja.palletsprojects.com/en/3.1.x/templates/#import-context-behavior
By default, included templates are passed the current context and imported templates are not.
{% import 'macros/header.html' as header with context %}
url_for
needs Request
object to generate correct url and the request object is in the context['request']