Skip to main content

Web Development Basics with Python & Flask

· 12 min read
Josh Kaplan

Getting started with web development can be daunting and overwhelming if you don't know where to start. This article will take you step-by-step through the basics of getting your first web application built.

Prerequisites

Install Python

Before getting started, make sure you have Python installed. I recommend using the Anaconda distribution of Python which comes bundled with a number of useful libraries and saves you the trouble of needing to install them later. If you have to choose, use Python 3 or newer.

Install Flask

You'll also want to make sure you have Flask installed. Flask is the web framework we'll be using to build our application. Once Python is installed, you can use Pip (Python's package manager) to install Flask by running

pip install Flask

Learn Some Command Line Basics

We're going to use the command line in our examples. For Windows that means Command Prompt, Powershell, or Ubuntu on Windows, for Mac that means Terminal. We'll be using Unix-like commands throughout the tutorial, so I recommend Ubuntu on Windows if you're using Windows.

If you've never used a command line before or need a refresher, there are a lot of tutorials out there, just Google it. But here's one for Linux to get you started.

Verify Your Installation

Verify Python is installed an is in your path:

python --version

You should see a version in the output. To verify Flask is installed, run:

python -c "import flask"

If you get no errors, you should be good to go.

Background

This section explains some general background knowledge on how the internet works and networking fundamentals. If you have some background in it or don't want to consume too much information right away, feel free to skip it and come back later.

How the internet works (briefly)

The most fundamental explanation of how the internet works is the client-server model. It goes something like this: you have a client which can be a laptop, mobile device, web browser, mobile app, etc. and a server (e.g. a computer on the internet). See below.

+----------+            +----------+
| | | |
| CLIENT | <========> | SERVER |
| | | |
+----------+ +----------+

The client (i.e. you) makes a request of the server (i.e. jdkaplan.com) by saying something like "Hey, I want to read the article about web development." And the jdkaplan.com server responds and says something like "Here you go, here's the article."

The language of that request and response is a bit more specific. But that's it. The client requests something and the server responds.

Internet Protocol

The primary mechanism computers use to talk to one another on the internet is called Internet Protocol (IP). Without getting into too much detail about how IP works, let's go over just a few basics.

All machines on the internet have an IP address of the form 123.123.123.123, where 123 can be a value between 0 and 255. (Note: We're just talking about IPv4 here. We won't be discussing IPv6). Under-the-hood all domain names, such as jdkaplan.com or ucf.edu map to an IP address using a system called DNS (Domain Name System, also out of scope for this post). The IP address 127.0.0.1 is special and tells a computer to refer to itself. So when we run a simple web server below, it will be accessible via the IP address 127.0.0.1. Most computer also have a special name for this: localhost. This is what you'll see in the examples below.

Different protocols exist on top of IP. For example TCP/IP is what the internet is built on. HTTP is a protocol built on top of TCP/IP. There are many different protocols that define the specifics of how two machines will talk to one other.

IP also uses something called ports. Ports are simply a number appended to the IP address or domain name. They can be a value between 0 and 65535. By default port 80 is used for HTTP and 443 is for HTTPS.

These ports usually aren't seen on the web, but you can go to https://jdkaplan.com:443 and everything will still work just fine. You're browser just hides the port from you when using default ports. In our examples below, we'll use alternate ports (e.g. 5000). This is mostly because ports below 1024 are reserved for privileged accounts. But you could also have multiple web servers running on a single machine using different ports.

HTTP

What that looks like, a little more specifically, is a protocol called HTTP or HyperText Transfer Protocol. That looks a bit more cryptic, but here's a simple example:

I ran the command curl -Lv jdkaplan.com/articles/index.html, which has a lot of output, but the important part looks something like this:

> GET /articles/index.html HTTP/1.1
> Host: jdkaplan.com
> User-Agent: curl/7.71.1
> Accept: */*

This says "I want to GET the /articles/index.html page from jdkaplan.com. The server then responds with something like

< HTTP/1.1 200 OK
< Content-Type: text/html
< Content-Length: 27726
< Connection: keep-alive
< Date: Mon, 07 Jun 2021 20:26:07 GMT
< Last-Modified: Mon, 07 Jun 2021 00:54:26 GMT
< ETag: "a6e163063f3137d5db457cfabb097736"
< Accept-Ranges: bytes
< Server: AmazonS3
< Vary: Accept-Encoding
< X-Cache: Miss from cloudfront
< Via: 1.1 f9ae6e33f293d8a4e80f48fca6093c68.cloudfront.net (CloudFront)

Followed by a whole bunch more data (mostly HTML and Javascript). The metadata above provides some information like a response status of 200 OK, meaning we got your request and everything is good and Content-Type: text/html which says the the data that will follow is HTML or HyperText Markup Language, which tells your browser how to render the page.

Getting Started

Open your command-line tool for your system (item #3 above) and navigate to the directory (a.k.a folder) where you want to write and store your code.

You can do this by running:

cd /path/to/your/project

Replace /path/to/your/project with the path to your project.

Creating a Webserver

Create a new file for our Flask application by running touch app.py.

Open up app.py in your text editor of choice and add the following:

from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
return 'Hello, World!'

if __name__ == '__main__':
app.run(host='127.0.0.1', port=5000, debug=False)

Viewing Your App

Now, you can navigate to http://localhost:5000 in your web browser of choice and you should see "Hello, World!" displayed on the screen.

Hello World app screenshot

Congratulations! You've built a website. Press CTRL + C to stop the server.

Rendering HTML

Now let's display something with a bit more formatting. To do this, we'll introduce HTML (Hypertext Markup Language), the language of the web. HTML is a markup language, meaning it's used to tell the browser where and how to render text. It isn't a programming language because it doesn't do anything.

To get started, create a directory called templates alongside your app.py file. You can do this with the mkdir templates command. Then create a file in that directory called hello.html by running touch hello.html.

Open up the HTML file using the text editor of your choice and add the following text (which we'll explain in a moment):

<!DOCTYPE html>
<html>
<head>
<title>Sample App</title>
</head>
<body>
<h1>Hello!</h1>
<p>This is a Flask application.</p>
</body>
</html>

Then, update your Flask webserver to render the new template instead of returning the "Hello, World! string. Note that Flask looks for templates in the templates directory.

from flask import Flask, render_template
app = Flask(__name__)

@app.route('/')
def hello_world():
return render_template('hello.html')

if __name__ == '__main__':
app.run(host='127.0.0.1', port=5000, debug=False)

Restart your webserver (the app.py program) and refresh your browser to see your new changes.

Understanding HTML

HTML is a markup language consisting of sets of tags. For example the <h1> tag in the example above is for level 1 headings and it formats text accordingly. Similarly, <p> is for paragraph. Most tags have an open tag (e.g. <h1>) and a close tag (e.g. </h1>) with some content or text in between. Some tags, such as <img src="myImage.jpg /> for inserting an image into a page don't use a close tag and instead use a single tag ending in /> because an image is either there or not. It doesn't need text in between open and close tags. We'll see that later with the <input /> tag.

To learn more about HTML, W3Schools.com has some useful tutorials and references.

Accepting User Input

Now let's make our web application actually do something. In this example, we want to ask the user for their name and instead of just saying Hello World, we'll use their name.

Now we need to make two updates to our HTML file. First, change Hello! to Hello, {{name}}!. The {{something}} syntax is used by Flask's templating engine to allow you to pass variables from your Python code into your HTML templates. Second, add the form, input, and button tags shown below to render a simple form.

<!DOCTYPE html>
<html>
<head>
<title>Sample App</title>
</head>
<body>
<h1>Hello, {{name}}!</h1>
<p>Enter your name:</p>
<form method="GET" action='/'>
<input type="text" name="myName" />
<button type="submit">Enter</button>
</form>
</body>
</html>

HTML forms are used to capture user input. A form has a method, usually GET or POST to indicate the HTTP method to use when submitting the form. For now, we'll use GET. The form tag also has an action parameter, used to define where to submit the form to. In this case we'll use / and talk more about routing later. The button is simply used to submit the form.

Once again, restart your webserver and refresh your browser to see the updated view.

Hello World app with input

Enter your name and submit the form to see it work.

Hello World app after form submit

You'll notice the URL changes after the form is submitted and now includes ?myName=Josh at the end. This is how the HTTP GET method works. It adds the form inputs to the URL.

GET vs. POST

Let's briefly explore the difference between GET and POST. First we need to update our HTML form to handle the form submit using the POST method.

<!DOCTYPE html>
<html>
<head>
<title>Sample App</title>
</head>
<body>
<h1>Hello, {{name}}!</h1>
<p>Enter your name:</p>
<form method="POST" action='/'>
<input type="text" name="myName" />
<button type="submit">Enter</button>
</form>
</body>
</html>

Then we need to update our Python code to handle the POST. We do this by explicitly telling Flask to allow the GET and the POST methods for our root (/) route (by default Flask only allows GET).

from flask import Flask, render_template, request
app = Flask(__name__)

@app.route('/', methods=['GET', 'POST'])
def hello_world():
# If the request was a POST (i.e. form submitted), get the user's name
if request.method == 'POST':
name = request.form.get('myName')
else:
name = None

# If they didn't enter a name, use "world"
if name is None or name == '':
name = 'World'

return render_template('hello.html', name=name)

if __name__ == '__main__':
app.run(host='127.0.0.1', port=5000, debug=False)

Note that we also have to write some logic to handle if the request is a POST (request.method == 'POST') because when the user first views the page in the browser, the method is GET, not POST, because that's how browsers work.

Cleaning Things Up

Finally, we can make this a little bit simpler to read and understand by separating the GET and POST handling into two separate functions. In this case, the hello_world function handles GET requests (i.e. when the user navigates to our page in their web browser) and the hello_user function handles POST requests.

from flask import Flask, render_template, request
app = Flask(__name__)

@app.route('/')
def hello_world():
return render_template('hello.html', name='World')

@app.route('/', methods=['POST'])
def hello_user():
name = request.form.get('myName')
return render_template('hello.html', name=name)

if __name__ == '__main__':
app.run(host='127.0.0.1', port=5000, debug=False)

Each of these functions is now much simpler to understand and the same hello.html template is user for both, but with different inputs.

Why POST vs. GET?

There are a few reasons to why you might choose GET or POST. GET is used when the request parameters should be part of the URL, for example in a search.

Take Google as an example, when I enter a search for, say, UCF the search term becomes part of the URL as a GET request. In fact, if you remove a lot of the extra content from the URL, you get something like this: https://www.google.com/search?q=ucf.

This means I could copy the URL from my search and share it with you. If you ever tried to share a link with someone and they didn't see the same results you did, it's possible the website was using a POST when they should be using a GET.

On the other hand, when I'm logging into a website or entering my credit card information, I really don't want my information being passed around in the URL for anyone to read. Thats why forms that collect sensitive information like passwords or credit card numbers use POST.

As a general rule of thumb, you want to use GET when the user is retrieving (or getting) information from the application and POST when they are sending (or posting) data to the application.

Summary

We went over the basics of how the web works and building a simple webserver using the Python programming language and the Flask web framework. We saw how to get and handle user input and display it back to the user.

In future tutorials I'll get into more depth, show how to style pages and use JavaScript to make you application dynamic and asynchronous.

Sample code for the examples discussed above can be found at https://github.com/josh-kaplan/web-development-examples.