Reputation: 355
In a FastAPI project, you can easily get data from a HTML form to the back-end. There are built-in ways to get data from a text input, a file upload, etc. However, dropdown menus don't seem to work in my project. FastAPI developer Tiangolo has addressed the issue after being requested and made a tutorial page including dropdown menus. I tried to follow the same steps as him but I cannot get data from a dropdown menu into my backend.
My code looks like this:
view.py
: contains the enum with the dropdown values, and generates the html template.class dropdownChoices(str, Enum):
water = "WATER"
fire = "FIRE"
electric = "ELECTRIC"
grass = "GRASS"
donut = "DONUT"
@router.get('/upload')
def upload(request: Request):
return templates.TemplateResponse('upload.html', context={'request': request, 'choices': [e.value for e in dropdownChoices]})
upload.html
: the template that will display my form, containing the dropdown menu.<form action="/upload" method="post" enctype="multipart/form-data">
<div class="form-group">
<label for="choices_dropdown">Choose:</label>
<select id="choices_dropdown" name="dropdown_choices">
{% for choice in choices %}
<option value={{choice}}>{{choice}}</option>
<!-- The choices are correctly displayed in the dropdown menu -->
{% endfor %}
</select>
</div>
<!-- More form actions including file upload and checkbox. These work. -->
<div class="form-group">
<label for="upload_file">Choose Upload File:</label>
<input type="file" class="form-control-file" name='upload_file' id="upload_file">
<input class="form-check-input" type="checkbox" name='overwrite_existing' id="flexCheckChecked"> Overwrite existing
</div>
<button id="upload" type='submit' class="btn btn-primary">Upload</button>
</form>
main.py
: handles data from the form.# Gets data from upload.html
@app.post("/upload")
async def handle_form(request: Request,
choice: str = "WATER",
upload_file: UploadFile = File(...),
overwrite_existing: bool = Form(False)):
print(choice) #does NOT work: always print default ("WATER")
print(overwrite_existing) #Works, prints true or false depending on input
contents = await upload_file.file.read() #Works, file is later read etc
return templates.TemplateResponse('upload.html', context={'request': request,
'choices': [e.value for e in view.dropdownChoices]})
I feel like I have followed the tutorial thoroughly, yet I always get the default choice. If I don't put a default choice in my handle_form()
method, I get nothing at all.
I don't understand why the user's choice form the dropdown menu is not transmitted like the rest.
Upvotes: 3
Views: 11777
Reputation: 491
You always get the default choice because you set that, and it isn't sent with a matching name.
choice: str = "WATER",
here, you say you expect choice
, not as the enum but as a plain str
, and you set the default value as literal str "WATER"
.
But on your front end, you send it with name as dropdown_choices
.
<select id="choices_dropdown" name="dropdown_choices">
Furthermore, with such a declaration, Fastapi would be expecting the value to be sent as a query parameter, but your front-end is sending it as part of the body as form data. For fastapi to properly take it where it is, and validate it correctly, you need to have both a matching name and type.
async def handle_form(request: Request,
dropdown_choices: dropdownChoices = Form(dropdownChoices.water),
upload_file: UploadFile = File(...),
overwrite_existing: bool = Form(False)):
The value is now declared with the proper name, and should be parsed and validated as the enum directly.The default value is also declared as a member of the enum, instead of an unrelated str.
Upvotes: 2
Reputation: 52892
Your name in the form is dropdown_choices
. Your name in your FastAPI endpoint definition is choice
. These need to be identical. You also want to tell FastAPI that this is a Form field as well (as you did with your checkbox):
choice: str = Form("WATER"),
You should also wrap the option value in ""
:
<option value="{{choice}}">
There is nothing magic about select boxes; data gets submitted in the usual ways - either through GET
or through POST
.
Upvotes: 1