How to submit HTML form value using FastAPI and Jinja2 Templates?

Option 1

You can have the category name defined as Form parameter in the backend, and submit a POST request from the frontend using an HTML <form>, as described in Method 1 of this answer.

app.py

from fastapi import FastAPI, Form, Request
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates

app = FastAPI()
templates = Jinja2Templates(directory='templates')

@app.post('/disable')
def disable_cat(cat_name: str = Form(...)):
    return f'{cat_name} category has been disabled.'

@app.get("https://stackoverflow.com/", response_class=HTMLResponse)
def main(request: Request):
    return templates.TemplateResponse('index.html', {'request': request})

templates/index.html

<!DOCTYPE html>
<html>
   <head>
      <meta charset="utf-8">
      <meta name="viewport" content="width=device-width, initial-scale=1">
   </head>
   <body>
      <h1>Disable a category</h1>
      <form method="post" action="/disable">
         <label for="cat_name">Enter a category name to disable:</label><br>
         <input type="text" id="cat_name" name="cat_name">
         <input class="submit" type="submit" value="Submit">
      </form>
   </body>
</html>

Option 2

You can have the category name declared as query parameter in your endpoint, and in the frontend use a similar approach to the one demonstrated in your question to convert the value form the form <input> element into a query parameter, and then add it to the query string of the URL (in the action attribute).

Note that the below uses a GET request in contrast to the above (in this case, you need to use @app.get() in the backend and <form method="get" ... in the frontend, which is the default method anyway). Beware that most browsers cache GET requests (i.e., saved in browser’s history), thus making them less secure compared to POST, as the data sent are part of the URL and visible to anyone who has access to the device. Thus, GET method should not be used when sending passwords or other sensitive information.

app.py

from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates

app = FastAPI()
templates = Jinja2Templates(directory='templates')

@app.get('/disable')
def disable_cat(cat_name: str):
    return f'{cat_name} category has been disabled.'

@app.get("https://stackoverflow.com/", response_class=HTMLResponse)
def main(request: Request):
    return templates.TemplateResponse('index.html', {'request': request})

templates/index.html

<!DOCTYPE html>
<html>
   <head>
      <meta charset="utf-8">
      <meta name="viewport" content="width=device-width, initial-scale=1">
   </head>
   <body>
      <h1>Disable a category</h1>
      <form method="get" id="myForm" action='/disable{{ cat_name }}'>
         <label for="cat_name">Enter a category name to disable:</label><br>
         <input type="text" id="cat_name" name="cat_name">
         <input class="submit" type="submit" value="Submit">
      </form>
   </body>
</html>

If you instead would like to use a POST request—which is a little safer than GET, as the parameters are not stored in the browser’s history, and which makes more sense when updating content/state on the server, compared to GET that should be used when requesting (not modifying) data—you can define the FastAPI endpoint with @app.post() and replace the above template with the below (similar to Method 2 of this answer), which submits the form using POST method after transforming the form data into query parameters:

<!DOCTYPE html>
<html>
   <head>
      <meta charset="utf-8">
      <meta name="viewport" content="width=device-width, initial-scale=1">
      <script>
         document.addEventListener('DOMContentLoaded', (event) => {
            document.getElementById("myForm").addEventListener("submit", function (e) {
               var myForm = document.getElementById('myForm');
               var qs = new URLSearchParams(new FormData(myForm)).toString();
               myForm.action = '/disable?' + qs;
            });
         });
      </script>
   </head>
   <body>
      <h1>Disable a category</h1>
      <form method="post" id="myForm">
         <label for="cat_name">Enter a category name to disable:</label><br>
         <input type="text" id="cat_name" name="cat_name">
         <input class="submit" type="submit" value="Submit">
      </form>
   </body>
</html>

Option 3

You can still have it defined as path parameter, and use JavaScript in the frontend to modify the action attribute of the <form>, by passing the value of the form <input> element as path parameter to the URL, similar to what has been described earlier.

app.py

from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates

app = FastAPI()
templates = Jinja2Templates(directory='templates')

@app.post('/disable/{name}')
def disable_cat(name: str):
    return f'{name} category has been disabled.'

@app.get("https://stackoverflow.com/", response_class=HTMLResponse)
def main(request: Request):
    return templates.TemplateResponse('index.html', {'request': request})

templates/index.html

<!DOCTYPE html>
<html>
   <head>
      <meta charset="utf-8">
      <meta name="viewport" content="width=device-width, initial-scale=1">
      <script>
         document.addEventListener('DOMContentLoaded', (event) => {
            document.getElementById("myForm").addEventListener("submit", function (e) {
               var myForm = document.getElementById('myForm');
               var catName = document.getElementById('catName').value;
               myForm.action = '/disable/' + catName;
            });
         });
      </script>
   </head>
   <body>
      <h1>Disable a category</h1>
      <form method="post" id="myForm">
         <label for="catName">Enter a category name to disable:</label><br>
         <input type="text" id="catName" name="catName">
         <input class="submit" type="submit" value="Submit">
      </form>
   </body>
</html>

Option 4

If you would like to prevent the page from reloading/redirecting when hitting the submit button of the HTML <form> and rather get the results in the same page, you can use Fetch API, a JavaScript interface/library, to make an asynchronous HTTP request, similar to this answer, as well as this answer and this answer. Additionally, one can call the Event.preventDefault() function, as described in this answer, to prevent the default action. The example below is based on the previous option (i.e., Option 3); however, the same approach below (i.e., making an asynchronous HTTP request) can also be used for Options 1 & 2 demonstrated earlier, if you would like to keep the browser from refreshing the page on <form> submission.

app.py

from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates

app = FastAPI()
templates = Jinja2Templates(directory='templates')

@app.post('/disable/{name}')
def disable_cat(name: str):
    return f'{name} category has been disabled.'

@app.get("https://stackoverflow.com/", response_class=HTMLResponse)
def main(request: Request):
    return templates.TemplateResponse('index.html', {'request': request})

templates/index.html

<!DOCTYPE html>
<html>
   <head>
      <meta charset="utf-8">
      <meta name="viewport" content="width=device-width, initial-scale=1">
      <script>
         document.addEventListener('DOMContentLoaded', (event) => {
            document.getElementById("myForm").addEventListener("submit", function (e) {
               e.preventDefault() // Cancel the default action
               var catName = document.getElementById('catName').value;
               fetch('/disable/' + catName, {
                     method: 'POST',
                  })
                  .then(resp => resp.text()) // or, resp.json(), etc.
                  .then(data => {
                     document.getElementById("response").innerHTML = data;
                  })
                  .catch(error => {
                     console.error(error);
                  });
            });
         });
      </script>
   </head>
   <body>
      <h1>Disable a category</h1>
      <form id="myForm">
         <label for="catName">Enter a category name to disable:</label><br>
         <input type="text" id="catName" name="catName">
         <input class="submit" type="submit" value="Submit">
      </form>
      <div id="response"></div>
   </body>
</html>

Leave a Comment