FastAPI Jinja:`url_for` 在宏中不起作用

问题描述 投票:0回答:1

在模板的另一个文件中定义的宏中使用

url_for
会引发“请求”的 KeyError。

考虑下面的树

.
|-- app.py
|-- static
|   |-- css
|   |   `-- style.css
|   |-- img
|   |   `-- logo.png
|   `-- js
|       |-- script.js
`-- templates
    |-- base.html
    |-- index.html
    `-- macros
        `-- header.html

还有这个应用程序.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})

如果在 base.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 %}

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


如果我尝试在另一个文件中定义头宏,就会出现问题

{# 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 %}

当我这样做时,我的 fastAPI 在 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'
python jinja2 fastapi
1个回答
0
投票

您应该导入

with context
,请参阅https://jinja.palletsprojects.com/en/3.1.x/templates/#import-context-behavior

默认情况下,包含的模板会传递当前上下文,而导入的模板则不会。

{% import 'macros/header.html' as header with context %}

url_for
需要
Request
对象来生成正确的url,请求对象位于
context['request']

© www.soinside.com 2019 - 2024. All rights reserved.