Lecture 34

Web app wrap-up

MCS 275 Spring 2024
Emily Dumas

View as:   Presentation   ·   PDF-exportable  ·   Printable

Lecture 34: Web app wrap-up

Reminders and announcements:

  • I caught up on homework solution posting!
  • Work on Project 4.
  • Project 4 is due 11:59pm central Friday April 26
  • Custom (non-SQLite) topics: Request by Monday
  • Autograder opens Monday April 22 (perfunctory checks only)

Today

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.)

Web app todo list

  • ☑ HTML mockup
  • ☑ Stylesheet
  • ☑ Learn a bit about Flask
  • ☑ Database schema & test data
  • ☑ Code/template to generate task list
  • ☑ "Wait" button modifies DB
  • ☑ "Wait" button doesn't crash server
  • ☑ Activate rest of buttons
  • ⌛ Add page to create new task
  • ☐ Create a front page

Web app todo list

  • ☑ HTML mockup
  • ☑ Stylesheet
  • ☑ Learn a bit about Flask
  • ☑ Database schema & test data
  • ☑ Code/template to generate task list
  • ☑ "Wait" button modifies DB
  • ☑ "Wait" button doesn't crash server
  • ☑ Activate rest of buttons
  • ⌛ Add page to create new task
  • ☐ Empty task lists get placeholder content
  • ☐ Create a front page
  • ☐ Create a single task view
  • ☐ Toggle show completed
  • ☐ Order of task list
  • ☐ Delete button
  • ☐ Error detection/handling

Offline update

I made the share button work, and other buttons disable themselves if irrelevant. Changes involved:

  • Update query builder (helper function in .py file)
  • Task list sections made with for-loop (reduce duplication)
  • Button display controlled by template if (new feature!)

Abort

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).

Forms

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).

Inputs name vs id

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>.

Textarea

<input type="text"> is typically for single-line answers.

Longer text entry (multi-line) should be handled with a <textarea> tag.

HTTP request types

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.

HTTP request types

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.

What form get request looks like

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")
))

Form styling

Larger text entry box for the description. No CSS needed!

The size attribute of an input of type "text" sets the width in characters.

Single task view

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.

After task creation

Natural to redirect from /task/new/ to the status page. But how?

We just INSERTed it so we don't know the woid.

Last inserted row

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.

Linking to single task view page

We can show the task id number in the list, and make it a link to the associated single task view page.

URLs in our app

  • /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.

Detect errors?

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.

Number of updated rows

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.

Hard-coded URLs

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's URL builder

flask.url_for(function_name) - convert a function name into the URL that triggers it.

Not-so-near misses

Some of the things you'd do differently in a "real" application:

  • Action history: We should probably log every action that is made in a separate table.
  • Accounts, roles, cookies: login page checks credentials against DB, sets browser cookie. Auth-required pages check for it, redirect to login page if not found.
  • JavaScript: e.g. to check for new tasks/updates in real time, make buttons perform POST requests without forms and auto-update display.
  • Pagination: Limit number of tasks we show. Links let you view the next or previous page. (Or maybe infinite scroll?)
  • Search: Limit task list to those matching criteria given by user (e.g. "MCS 275" in description")
  • Deploy: Domain name, server, ...

References

Revision history

  • 2023-04-12 Finalization of the 2023 lecture this was based on
  • 2024-04-03 Initial publication