MCS 275 Spring 2024
Emily Dumas
Reminders and announcements:
This is the last in our contiguous lecture series focused on writing a Flask+SQLite application.
(We may revisit this topic a bit in the last week.)
I made the share button work, and other buttons disable themselves if irrelevant. Changes involved:
.py
file)if
(new feature!)flask.abort(http_error_code)
- Immediately stop and return a HTTP error code (usually 400 bad request, 401 not authorized, 403 forbidden, or 404 not found).
Interactive elements in an HTML document (text entry, checkbox, dropdown list, etc.)
jsfiddle is a nice way to test out form designs (for code that can be public).
Each form input should have both a name
and id
attribute. Usually they are
equal, but they have separate roles:
name
is what this value is called when submitted to the server.id
is used to match an input with its <label>
.<input type="text">
is typically for single-line answers.
Longer text entry (multi-line) should be handled with a <textarea>
tag.
GET
- load a resource, the only action we've considered so far.
GET
requests are supposed to be idempotent, meaning repeating the same request
multiple times has the same effect as doing it once.
POST
- submit data and/or request an action.
POST
requests are not expected to be idempotent. Browsers typically prevent reloading a
POST
request, for example.
By default, forms use a GET
request and put form data in the URL.
This is usually a bad idea, and a POST
request is more appropriate.
Easy change: Add method="post"
attribute to the <form>
tag.
Form values become query parameters, e.g.
https://example.com/formsub/?full=David%20Dumas&nick=deedee
Many ascii characters appear verbatim but others* become %
escape
sequences with two hex digits. Flask decodes these and makes the parameters available as
flask.request.values.get(name)
.
* The precise encoding scheme is specified in RFC3986. Python's built-in
urllib.parse
module has functions that perform this type of encoding/decoding:
urllib.parse.quote
and urllib.parse.unquote
. But when using Flask, you
never need these!
Form values are made available to the function handling submission through
flask.request.values.get(name)
.
Note that a Flask route must explicitly declare that it accepts POST requests:
from flask import Flask, request
# ... app setup ...
@app.route('/registernick',methods = ['POST', 'GET'])
def record_fullname_and_nickname():
print("Received nickname {}".format(
request.values.get("nick")
))
Larger text entry box for the description. No CSS needed!
The size
attribute of an input
of type "text"
sets the width in characters.
Make /task/5/
show you info about task 5.
New template, new route in the main python program.
Links to owner's list of tasks.
Natural to redirect from /task/new/
to the status page. But how?
We just INSERT
ed it so we don't know the woid
.
After a single-row INSERT
, how to get the primary key of the new row?
SELECT last_insert_rowid();
Implicitly refers to the most-recently executed INSERT
on this connection.
We can show the task id number in the list, and make it a link to the associated single task view page.
/tasks/<username>/
- (GET) Task list (view)
/task/new/
- (GET) Form to make new task (view)/task/new/submit
- (POST) Form target/task/<int:taskid>/update
- (GET*) Change task attributes/task/<int:taskid>/delete
- (GET*) Remove this task/task/<int:taskid>/
- (GET) Single task info (view)* These should really be POST but we would need to use javascript or a different
button markup to do it because <a>
tags generate a GET request.
We perform database queries. They might fail for some reason and raise an exception.
But we also expect some of these queries to change exactly one row. We don't check that.
An UPDATE
might match any number of rows (e.g. 0, 1, 50). The query
SELECT changes();
returns the number of rows changed by the last UPDATE
on this connection.
Right now, our templates contain explicit reference to the URLs our application uses.
These are also declared in the Python source (@app.route(...)
).
It would be nice to have them in only one place, for ease of change or maintenance.
flask.url_for(function_name)
- convert a function name into the URL that triggers it.
Some of the things you'd do differently in a "real" application: