I am using bottle, and I want to handle a put request from HTML and update a nested dictionary. I know I can do front-end validation but I am currently working on an assignment where this is a requirement.
here is my dictionary of items:
items = {
'daf-394kd-d-39823-393': {
"name": "a", "lastname": "a"
},
'daf-djfk3-32983-393dg': {
"name": "b", "lastname": "b"
}
}
and here is my route:
@put("/items/<item_id>")
def _(item_id):
new_name = request.forms.get("name")
new_lastname = request.forms.get("lastname")
items[item_id] = {"name": new_name, "lastname": new_lastname}
return redirect("/items")
and here is my HTML code:
<form action="/items" method="post">
<input type="hidden" name="_method" value="put">
<label for="">NAME</label>
<input name="name" type="text" placeholder="name">
<label for="">LAST NAME</label><br>
<input name="lastname" type="text" placeholder="lastname"><br><br>
<button>Update</button>
</form>
I tried to use a hidden input in order to change the post method to put but it doesn't seem to work.
Surprisingly, I don't see anything in the documentation, GitHub issues, Stack Overflow, or the internet in general about how to add a hidden field to your form to tell Bottle to call the PUT handler for a form submission. Workarounds are to use JS/AJAX to send a PUT request or to use a POST with PUT (update) semantics:
server.py
from bottle import redirect, request, route, run, template
items = {
"daf-394kd-d-39823-393": {
"name": "a",
"lastname": "a"
},
"daf-djfk3-32983-393dg": {
"name": "b",
"lastname": "b"
}
}
@route("/items/<item_id>", method="POST")
def items_update(item_id):
new_name = request.forms.get("name")
new_lastname = request.forms.get("lastname")
items[item_id] = {"name": new_name, "lastname": new_lastname}
return redirect("/items")
@route("/items")
def items_all():
return template("items.tpl", items=items)
run(host="localhost", port=8080)
items.tpl
<!DOCTYPE html>
<html>
<body>
% for k, v in items.items():
<div>
<div>
id: {{k}}, first name: '{{v["name"]}}', last name: '{{v["lastname"]}}'
</div>
<form action="/items/{{k}}" method="post">
<label for="name">NAME</label>
<input name="name" placeholder="name">
<label for="lastname">LAST NAME</label>
<input name="lastname" placeholder="lastname">
<input type="submit">
</form>
</div>
% end
</body>
</html>
This is less than satisfying since the same route would need explicit branches to differentiate actions:
server.py
(partial)@route("/items/<item_id>", method="POST")
def items_update_delete(item_id):
if request.forms.get("_method") == "put":
new_name = request.forms.get("name")
new_lastname = request.forms.get("lastname")
items[item_id] = {"name": new_name, "lastname": new_lastname}
elif request.forms.get("_method") == "delete":
del items[item_id]
return redirect("/items")
index.tpl
<!DOCTYPE html>
<html>
<body>
% for k, v in items.items():
<div>
<div>
id: {{k}}, first name: '{{v["name"]}}', last name: '{{v["lastname"]}}'
</div>
<form action="/items/{{k}}" method="post">
<input type="hidden" name="_method" value="put" />
<label for="name">NAME</label>
<input name="name" placeholder="name">
<label for="lastname">LAST NAME</label>
<input name="lastname" placeholder="lastname">
<input type="submit">
</form>
<form action="/items/{{k}}" method="post">
<input type="hidden" name="_method" value="delete" />
<input type="submit" value="Delete">
</form>
</div>
% end
</body>
</html>
I'd be happy to see a more idiomatic answer that shows the correct hidden field for triggering Bottle's PUT route and I'll update this answer if I come across it. Opening an issue in Bottle's GitHub repo might be worth doing.