Lab 8: Introduction to Web Applications

Part 2: More Flask

If you find typos or problems with the lab instructions, please let us know.

Ok, a web page that prints “Hello World” is not really very compelling. Let’s do something a bit more interesting.

In part 2 of the SPIS webapps tutorial, we’ll learn about how to:

  • Perform a calculation based on a parameter that’s in the url

  • Start building a larger multi-page web application

Note: The beginning of Part 2 is individual.

Add an ftoc function to hello_name.py

We’ve already seen a function that converts Farenheit to Celsius. Here it is again:

def ftoc(ftemp):

return (ftemp - 32.0) * (5.0 / 9.0)

Let’s add this to our hello_name.py file, so that we have the following:

from flask import Flask

app = Flask(__name__)


@app.route("/")

def hello():

return "Hello World!"


def ftoc(ftemp):

return (ftemp - 32.0) * (5.0 / 9.0)


if __name__ == "__main__":

app.run(host='0.0.0.0')

Now, you can test your function by doing this:

  • Type python into the terminal to start the python shell

  • Once in the python shell, type from hello_name import ftoc (replacing hello_name with your file name without the .py). Think, what does from hello_name import ftoc do?

  • Try various values of ftoc, such as ftoc(212.0) and ftoc(32.0) in the python shell

If your ftoc function works, then you are good to go to the next step where we hook this up to the web app. Make sure to quit the python shell with quit().

Hooking this up to the web app

To make this code work with the web app, we need to add a few more lines of code.

First, we’ll just do it and see what it does, then we’ll unpack each line of code and explain its purpose.

from flask import Flask

app = Flask(__name__)


@app.route("/")

def hello():

return "Hello World!"


def ftoc(ftemp):

return (ftemp - 32.0) * (5.0 / 9.0)


@app.route('/ftoc/<ftemp_str>')

def convert_ftoc(ftemp_str):

ftemp = 0.0

try:

ftemp = float(ftemp_str)

ctemp = ftoc(ftemp)

return "In Farenheit: " + ftemp_str + " In Celsius " + str(ctemp)

except ValueError:

return "Sorry. Could not convert " + ftemp_str + " to a number"


if __name__ == "__main__":

app.run(host='0.0.0.0')

Before running this new code, we're going to want to open the mini Repl.it browser window in its own window, so that we can edit the URL to add parameters into it. To do this, copy the URL in the mini Repl.it browser window, open a new tab in your browser, and paste in the URL. Alternatively, you can click "Open in a new tab", which will do the same thing.

After doing that, try running your new code again (by typing python filename in the terminal).

You should now be able to put in URLs such as the ones below, where the last part is a Farenheit temperature you want to convert to Celsius:

Note: Make sure to replace the first part of these URLs (https://YOUR_URL) with your own URL that you copied and put into a new tab.

Try it and see what you get. Also try some URLs where the last part is not valid, e.g.

If you’ve gotten this far, you’ve gotten a great start.

Check in with your partner to make sure that both of you have succeeded up to this point.

How does it work?

One of the keys to understanding how Flask works is to focus first on these lines of code in hello_name.py.

@app.route('/')

@app.route('/ftoc/<ftemp_str>')

The parts of our code that start with the @ sign are called decorators. In this case, they come right before a function definition, and they tell Python to do something special with the function definition that follows.

In this case, the @app.route(path) decorator indicates that URLs that end in path should be routed to the function that follows.

In the path parameter of the app.route decorator, anything in angle brackets, such as <ftemp_str> stands for a value that is passed into the function as a parameter. These are always of type str (string) and therefore have to be converted if they are going to be used as something else (e.g. float, int, etc.).

Now try some things on your own.

Try the following for practice:

  • Add a function that converts miles to kilometers, i.e. def miles_to_km(miles): … that just like the ftoc function, is a “pure” function that “doesn’t know its part of a web app”.

  • Then, add a function with a decorator @app.route('/mtokm/<miles_str>') that will convert the miles_str into a number, pass it into the function, and produce output. You can use the convert_ftoc function as an example to build on.

  • If that works, then see if you can write your own function. Note that you can use a route such as @app.route('some-route/<a>/<b>/<c>') if you wanted to have a function that took three inputs instead of just one.

Note: For your new changes to be rendered, you must close your server and then rerun it. You can close it with CTRL+C (Holding 'Control' and then clicking 'C'), and you can run it again with the usual python filename.


Templates

Our next steps will be to learn a bit about:

  • HTML and CSS, the languages we use to make our web pages look like “real web pages” instead of just plain text.

  • Both of those are for our next lesson, but we can at least get a head start if you’ve finished this material early.

HTML and CSS

So, at the moment, the values we are returning from our functions are plain text that shows up in the browser. That’s fine for getting started, but eventually we’d like something that looks like a “real web page”. For that, we’ll need to learn a little bit about HTML and CSS.

Get started on learning HTML and CSS now using one of the best resources on the web, the site: w3schools

And do the following:

  • Visit their HTML tutorial and read/skim over the first seven lessons

  • After learning some HTML, learn a bit of CSS by reading over the first four lessons

  • After each lesson check in with your pair partner to make sure both of you are understanding the content.

If you and your partner want to learn more about HTML/CSS you can spend more time on the website.

Pair Programming

At this point in the lab there is about to be a deeper dive into webapps content. Begin working with your partner together for the rest of the lab and before continuing, make sure that both of you have a solid understanding of the beginning content of this lab. After both of you feel that you have a good understanding of the beginning content of the lab, continue through the rest of the lab, making sure both of you have a good understanding of the material.

Using Templates

Now that we know a bit about HTML, we can try building a multi page application that uses HTML on each page.

Here is an example of how that would look. You need to create a subdirectory (folder) called templates. In templates, the first file you should create and store is one called layout.html.

The layout.html file should look like this:

<!doctype html>

<html>

<head>

<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">

<title> {% block title %}{% endblock %} - My Webpage</title>

</head>


<body>

<div id="content">{% block content %}{% endblock %}</div>

</body>

</html>


The file layout.html defines a template for every page of your web application.

There are two parts of this file that are, strictly speaking, not HTML code. Those are:

  • {% block title %}{% endblock %}

  • {% block content %}{% endblock %}

These are placeholders where some other HTML code will be inserted, and the syntax is defined by a system called Jinja2, which is part of the Flask framework. That’s all you really need to know about Jinja2 for now, but if you are curious to learn more, there is documentation here: http://jinja.pocoo.org/.

The title and content blocks for each of our pages are going to be defined in additional files in our templates directory. These files will be different webpages, each with their own title and content blocks.

Creating a template for the home page

Now you can define the templates for the rest of the pages in your web application. Let’s make a web application with three pages: one that converts fahrenheit to celsius, another than converts celsius to fahrenheit, and a third that converts miles to kilometers.

The template for the home page will be called home.html and should be in the templates subdirectory. It will look like this:

{% extends "layout.html" %}


{% block title %}Home{% endblock %}


{% block content %}

<h1>Home</h1>

<ul>

<li><a href="/ctof">Convert Celsius to Fahrenheit</a></li>

<li><a href="/ftoc">Convert Fahrenheit to Celsius</a></li>

<li><a href="/mtokm">Convert Miles to Kilometers</a></li>

</ul>

{% endblock %}

Creating templates for pages with user input

Then you’ll have three more templates for the pages where you ask the user for the input for each of these calculations. We will be using the <form> and <input> tags to create the fields. Take note of the attributes we add. Here are the first two, which you should call ctof.html and ftoc.html. Each of these should be stored under your templates directory.

Here’s ctof.html:

{% extends "layout.html" %}


{% block title %}Convert ctof{% endblock %}


{% block content %}


<p>Enter a temperature and click "submit" to convert to Fahrenheit</p>


<form action="/ctof-result">

Celsius Temp:<br>

<input type="text" name="ctemp" value="20.0">

<input type="submit" value="Submit">

</form>


{% endblock %}

Notice that the action of the form is to redirect you to a different webpage. After we submit a value, we want to go to the page where we can get the result! We’ll create this page for ctof and the others later in this lab.

Here’s ftoc.html:

{% extends "layout.html" %}


{% block title %}Convert ftoc{% endblock %}


{% block content %}


<p>Enter a temperature and click "submit" to convert to Celsius</p>


<form action="/ftoc-result">

Fahrenheit Temp:<br>

<input type="text" name="ftemp" value="68.0">

<input type="submit" value="Submit">

</form>


{% endblock %}


You’ll also need a file in templates called mtokm.html. For now, just enter the following html code as a placeholder—getting that one to work is left as an exercise for you.

Here’s mtokm.html:

{% extends "layout.html" %}


{% block title %}Convert Miles to Kilometers{% endblock %}


{% block content %}


<p>Coming soon...</p>


{% endblock %}


Creating templates for the results pages

Finally you’ll need three templates for the HTML for the pages that come up after you do the conversions.

Those will be called ftoc_result.html, ctof_result.html and mtokm_result.html. Here’s what the first two of those will look like:

Here’s ftoc_result.html:

{% extends "layout.html" %}


{% block title %}Result of converting Fahrenheit to Celsius{% endblock %}


{% block content %}

<p> In Fahrenheit: {{ ftemp }}. In Celsius: {{ ctemp }} </p>

{% endblock %}


Here’s ctof_result.html:

{% extends "layout.html" %}


{% block title %}Result of converting Celsius to Fahrenheit{% endblock %}


{% block content %}

<p> In Celsius: {{ ctemp }}. In Fahrenheit: {{ ftemp }} </p>

{% endblock %}


Finally, you’ll also need a mtokm_result.html file. Here’s a placeholder for it. The final content is up to you to fill in:

{% extends "layout.html" %}


{% block title %}Result of converting Miles to Kilometers{% endblock %}


{% block content %}

<p>Coming soon...</p>

{% endblock %}


Adding a style.css file

You should also create a subdirectory of the top of your repository called static, at the same level as your hello_name.py file, and as a sibling of your templates directory (not inside it.)

Inside that folder, put a file called style.css. This file will contain rules for the fonts, colors, spacing, and layout for your web page, expressed in a language called CSS, which stands for Cascading Style Sheets.

Below is a basic style.css file. You can learn more about CSS rules at w3schools and experiment with the style if you like.

body {

background-color: #eef;

color: black;

}

Other things that might go into our static directory later on include things like images (.png, .jpg files) that we may want to display on our web pages.


Changes to hello_name.py to use templates

Finally, we are ready for the changes to our hello_name.py that allow us to use these templates.

For each of the different URLs that our web application can serve, we will still write a function, just like before. But this time, instead of directly returning the string that makes up the web page, we’ll call the Flask function render_template, like this:

@app.route('/')

def render_home():

return render_template('home.html')

Below is a complete example of the code that we’ll want to put into our hello_name.py file. But note, there are a LOT of changes. Go slowly with your partner to ensure that your hello_name.py matches the complete example. Take note of all the changes that are made and follow the comments. Explain to your partner what is going on in the render_ftoc_result() function to ensure both of you understand what the function does. Ask a mentor if you’re unsure.

Note the the additional import statements that are needed:

import os

from flask import Flask, url_for, render_template, request


app = Flask(__name__)


@app.route('/')

def render_main():

return render_template('home.html')


@app.route('/ctof')

def render_ctof():

return render_template('ctof.html')


@app.route('/ftoc')

def render_ftoc():

return render_template('ftoc.html')


@app.route('/mtokm')

def render_mtokm():

return render_template('mtokm.html')

@app.route('/ftoc-result')

def render_ftoc_result():

try:

ftemp_result = float(request.args['ftemp'])

ctemp_result = ftoc(ftemp_result)

return render_template('ftoc_result.html', ftemp=ftemp_result, ctemp=ctemp_result)

except ValueError:

return "Sorry: something went wrong."


@app.route('/ctof-result')

def render_ctof_result():

try:

ctemp_result = float(request.args['ctemp'])

ftemp_result = ctof(ctemp_result)

return render_template('ctof_result.html', ctemp=ctemp_result, ftemp=ftemp_result)

except ValueError:

return "Sorry: something went wrong."


@app.route('/mtokm-result')

def render_mtokm_result():

try:

# You'll need some code here, and maybe some extra parameters in render_template below...

return render_template('mtokm.html')

except ValueError:

return "Sorry: something went wrong."


def ftoc(ftemp):

return (ftemp - 32.0) * (5.0 / 9.0)

def ctof(ftemp):

return -42.0 # replace with correct formula


# You'll probably want a basic function here to convert miles to kilometers too...

if __name__=="__main__":

app.run(host='0.0.0.0')


Be sure to test your web app

Make sure you run and spend time on your web app to ensure that everything is working how you want it to! For example, make sure that values are being converted correctly. You want to make sure that after you click submit, it directs you to a different webpage, showing you the information you want it to. As you recall from previous labs, testing is an important concept we need to practice and know.

A Quick Disclaimer: Before moving onto part 2.5, let's go over a little disclaimer. Repl.it automatically puts your code onto a server somewhere, which allows you to access it anywhere online with the https://YOUR_URL/ (this format: https://spis20-lab08-name1-name2.replit-username.repl.co/) link. But in the future, you'll be coding on your own computer and not on Repl.it; when on your own computer, running the flask code will not automatically put your web app onto a server somewhere (meaning that you cannot access it online from anywhere). Instead, running the flask code will put it on a "local host", basically meaning that your computer becomes a "virtual server" for the code (aka local server). This basically means that you'll only be able to access the web app on the computer that you used to run it. Obviously, when we create a web app, we want to have it accessible by anyone with internet access -- thus we need to figure out a way to put it on a web server. We'll learn how to do this in Part 3 of Lab 8, but for now, just recognize that when you code in the future, it'll be a little different than how you're doing it on Repl.it.

You might be asking, what's the point of having our web app on a "local host" (aka local server) if nobody else can access it? The main purpose is simply to be able to test the web app on a browser before allowing access to everyone by putting it online.

Ok, now let's move onto Part 2.5 .