Web UI for our return form
--------------------------
Let's finish this chapter by putting together a simple (but ugly) web UI that allows a user to generate a return form.
We'll be using Flask for this exercise, and to keep things simple, we'll skip the JavaScript bit and *generate* all HTML on the server side. Flask makes this fairly easy as it integrates with the Jinja 2 templating language.
Products
========
To start things off, as an example, let's see how we could generate and display an HTML table consisting of Python data. Here's the relevant Python snippet:
.. code-block:: python
from flask import Flask, render_template, request, make_response
app = Flask(__name__)
@app.route("/products/", methods=['GET'])
def products():
my_list = [{'a': 1, 'b': 2}, {'a': 3, 'b': 4}]
return render_template('products.html', my_list=my_list)
This returns an HTML file based on the template "products.html" which Flask will look for in the "templates" directory, but with a twist: it generates the HTML based on the contents of "my_list". The HTML template could look e.g. like the following:
.. code-block:: html
Products
List of all products
a |
b |
{% for elem in my_list %}
{{ elem.a }} |
{{ elem.b }} |
{% endfor %}
This makes use of the Jinja 2 *for* statement which will loop through a list provided to the template. In this case, it'll access the dictionary keys "a" and "b" of the input list and display the numbers 1, 2, 3 and 4 in the table.
You can include your SQLite database in your Flask application by calling the relevant functions at the top level, e.g.:
.. code-block:: python
from flask import Flask, render_template, request, make_response
app = Flask(__name__)
import sqlite3
db = sqlite3.connect('mydb')
cursor = db.cursor()
# the rest of the code goes here
*Exercise*: Create the "products" page. Query your database for the products. Turn the result to a dictionary, write an HTML template and pass your data to your template. The page should display all the columns for all your products.
Orders
======
Now, it would be nice to be able to see all the orders by a customer. It would furthermore be nice to be able to write a URL like e.g. "http://127.0.0.1:5000/orders?customer_id=123" and get an overview of the orders made by customer 123. Let's do this next.
The part in the URL after the "?" is the query string and is accessible in Flask using the function "request.args.get()". In our case, the following line is what we need:
.. code-block:: python
customer_id = request.args.get('customer_id', 1) # default to 1 if not given
*Exercise*: Create the Python handler for displaying the orders of a customer. Perform the relevant SQL query. Write an HTML template and provide the relevant data to the template. Also have the HTML display the customer ID for which the orders are shown. Do this by passing the customer_id variable to the template. You'll then be able to access the value in HTML using e.g. {{ customer_id }}.
Now that we're able to see what orders a customer has made, it would be nice to see the details of an order.
*Exercise*: In your table showing the orders, add another column which is a link to a more detailed page about the order. (We don't have the page yet so clicking on the link would make Flask return 404; this is fine for now.) You can create a suitable link using e.g. Show details.
Order details
=============
We can now click on a link that would show order details but that page doesn't exist yet so let's create it. To make things more interesting, we can imagine we're writing this page for the customer with the goal that the customer should be able to start the return process from this page. We should have a flow that looks like this:
.. image:: ../material/retail/ordflow.png
In other words, the customer would first select which items to return, then enter another page where they can provide a reason for the return, and submitting the form on that page will trigger a database update to enter the data about the return and send a PDF to the customer to print and include in the return package.
The order details page could look e.g. like this:
.. image:: ../material/retail/order.png
We have a few elements here:
* The order ID is shown
* A table listing all the products for the order is shown
* This page includes a *form*; the user can select a number of products using the check boxes and submit the selection to the server by pressing the button labelled "Return"
We should have a grip on displaying the order ID and the table without the check boxes by now. We can put together a form that sends the contents of the check boxes as well as the order ID using e.g. the following HTML:
.. code-block:: html
:linenos:
That is, we do the following:
* All the elements that are part of the form, including the submit button and the check boxes must be within the