This is not FastAPI's issue, but rather Starlette's issue (i.e., request.url_for()
receives path
parameters, not query
parameters). So, nspired by #560 and #1385, I have created the following working example for calling FastAPI routes from within Jinja2 templates, and passing query
params (alone or along with path
params as well).
Please note that this is a feature that is probably about to be introduced into the next version of Starlette #1385. Thus, best to use that one, when it is out.
app.py
import uvicorn
from fastapi import FastAPI, Response
from fastapi.templating import Jinja2Templates
from fastapi import Request
from fastapi.responses import HTMLResponse
import urllib
app = FastAPI()
class CustomURLProcessor:
def __init__(self):
self.path = ""
self.request = None
def url_for(self, request: Request, name: str, **params: str):
self.path = request.url_for(name, **params)
self.request = request
return self
def include_query_params(self, **params: str):
parsed = list(urllib.parse.urlparse(self.path))
parsed[4] = urllib.parse.urlencode(params)
return urllib.parse.urlunparse(parsed)
templates = Jinja2Templates(directory='templates')
templates.env.globals['CustomURLProcessor'] = CustomURLProcessor
@app.get('/updates/page/{page_no}/item/{item_id}')
def updates(request: Request, page_no: int, item_id: int, user: str, msg: str):
return templates.TemplateResponse("item.html", {"request": request, "page_no": page_no, "item_id":item_id, "user": user, "msg": msg})
@app.get('/updates_query_only')
def updates_query_only(request: Request, user: str, msg: str):
return templates.TemplateResponse("item.html", {"request": request, "user": user, "msg": msg})
@app.get('/')
def index(request: Request):
return templates.TemplateResponse("index.html", {"request": request})
if __name__ == '__main__':
uvicorn.run(app, host='127.0.0.1', port=8000, debug=True)
templates/index.html
<!DOCTYPE html>
<html>
<body>
<div class="container">
{% set cu = CustomURLProcessor() %}
{% set _url = cu.url_for(request, 'updates', page_no=5, item_id=3).include_query_params(user='foo', msg='bar') %}
<!-- if only query params required, use as follows: -->
{# {% set _url = cu.url_for(request, 'updates_query_only').include_query_params(user='foo', msg='bar') %} #}
<iframe src="{{ _url }}" width = 300 height = 300 style= "border: none;"></iframe>
</div>
</body>
</html>
templates/item.html
<!DOCTYPE html>
<html>
<body>
<h1>Page No {{ page_no }}</h1>
<h2>Item {{ item_id }}</h2>
<h3>{{ user }}</h3>
<h4>{{ msg }}</h4>
</body>
</html>
Update
You can now use Starlette's starlette.datastructures.URL
, which provides a method to include_query_params
. Example below:
In app.py import the URL
class and make it accessible from Jinja2 templates:
from starlette.datastructures import URL
templates = Jinja2Templates(directory="templates")
templates.env.globals['URL'] = URL
In templates/item.html use as follows:
<!DOCTYPE html>
<html>
<body>
<div class="container">
<iframe src="{{ URL(url_for('updates', page_no=5, item_id=3)).include_query_params(user='foo', msg='bar') }}" width = 300 height = 300 style= "border: none;"></iframe>
</div>
</body>
</html>