Mastering Flask: A Comprehensive Web Development Series for Python Enthusiasts
Article 1: Building Your First Web App with Python and Flask
Be Sure to Complete the Series
In this Flask web development series, we'll dive deeper into Flask and cover topics that build on the basics introduced in the first article. We'll explore more advanced features of Flask, including database integration, user authentication, and RESTful APIs. We'll also show you how to use Docker to containerize your Flask application, simplify deployment, and set up CI/CD with GitHub Actions to automate testing and deployment. By following this series, you'll gain a comprehensive understanding of Flask and its ecosystem, and easily build robust, scalable web applications. So follow the entire series to get the most out of your Flask learning journey.
Learning Outcomes
By the end of this article, you will have learned the basics of web development with Flask, including how to define routes, create a minimal application, and render templates. You will understand how to use Flask to build a simple web application that can handle user requests and responses. You'll also gain an understanding of the Flask web framework and its benefits, as well as how to install and set up Flask on your local machine. These fundamental skills will provide a solid foundation for you to continue building more complex web applications with Flask.
What is Flask?
Flask is a Web Application Framework and it is written in Python. Flask is used to build websites and RESTful APIs over the internet. As Flask is easy to learn and lightweight, it is excellent for junior developers. However, Flask provides a lot of room for customization that experienced developers can leverage to build extremely complex web applications from scratch. A popular framework like Django comes with pre-configured packages that are not always necessary and make the application unnecessarily heavy.
Flask can be described as a web framework that is lightweight, highly customizable, and straightforward to learn. It is a versatile framework that can be used for developing full-stack applications as well as RESTful web applications.
Flask is dependent on Jinja templates and werkzeug toolkit. Jinja is a template engine for Python. Werkzeug is a WSGI application library.
Do You Have These?
Software | Installation/Download Link |
Python | https://www.python.org/downloads/ |
VS Code | https://code.visualstudio.com/download |
First Flask App
Open a directory where you would want to save your project. When working on Python projects, it is best to create an isolated environment. This allows the developer to maintain multiple different versions of packages in different projects without letting those clash with each other. Python venv module is the easiest and most sufficient for small projects.
mkdir mastering-flask && cd $_ # create and cd into project directory
python3 -m venv flask # Create a virtual env named flask
. flask/bin/activate # Activate flask virtual env
I am implementing version control using Git and utilizing GitHub to enable code sharing with others. To facilitate this, each topic covered in the series will be stored in its own branch within Git. This will allow others to easily access and review the actual code when necessary.
git init
git checkout -b article-one
Create app.py and requirements.txt at the root of the project. Creating requirements.txt let's maintain and install dependencies of a project easily. app.py will hold all the code. The requirements.txt file would only need to include Flask, as it is the only dependency required for this article.
pip install -r requirements.txt # install dependencies
With Flask installed in the virtual environment, it is time to create a web server in Flask.
from flask import Flask
app = Flask(__name__)
The first step is to import the Flask class. Flask class inherits WSGI features. An instance of Flask class provides the actual WSGI application.
In Flask, app = Flask(__name__)
is a common line of code used to create a new instance of the Flask class. The __name__
argument is a special Python variable that represents the name of the current module. When we pass this variable to the Flask constructor, it tells Flask where to find the application's resources, such as templates and static files. By creating a new instance of the Flask class with this line of code, we are initializing a new Flask application and storing it in the app
variable for later use.
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return 'Hello Developers!'
route is a decorator. It tells Flask which function to execute when a specific URL endpoint receives a request. In this case, making a HTTP request to '/' url of the server will return a text with value 'Hello Developers!'
But how to start a server?
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return 'Hello Developers!'
if __name__ == '__main__':
app.run(debug=True)
python3 app.py
The if __name__ == '__main__':
line ensures that the code inside the block only runs if the script is executed directly, rather than being imported as a module.
app.run
function starts the web server. debug=True
is to make sure debugging is enabled as it is currently in the development or rather learning stage. It also reloads the server for every code change.
It is possible to define different HTTP methods in the route decorator. Other possible options are
Route | Options | Implementation |
/ | HTTP Methods | @app.route('/', methods=['POST']) def index(): return 'You have made a POST request' |
@app.route('/', methods=['GET']) def index(): return 'Hello Developers!' | ||
/users/<string:name> | Variable Rules (converter types: string, int, float, path, uuid). Path accepts string but with /. | @app.route('/users/<string:name>') def userName(name): return f'Name: {name}' |
/path/<path:name> | Variable Rules - Path | @app.route('/path/<path:path>') def path(path): return f'Entered subpath: {path}' |
/my-name or /my-name/ | Unique URLs / Redirection Behavior | @app.route('/my-name') def myName(): return 'Ritwik' |
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return 'Hello Developers!'
@app.route('/', methods=['POST'])
def postIndex():
return 'You have made a POST request'
@app.route('/users/<string:name>')
def userName(name):
return f'Name: {name}'
@app.route('/path/<path:path>')
def path(path):
return f'Entered subpath: {path}'
@app.route('/my-name')
def myName():
return 'Ritwik'
if __name__ == '__main__':
app.run(debug=True)
Different HTTP methods on the same URL can be used for different outcomes.
If URL /my-name has a trailing slash /, requesting /my-name will result in Flask automatically redirecting the browser to /my-name/ URL. If /my-name/ is the requested endpoint and the URL does not have a trailing slash, Flask will throw a 404 not found error.
Rendering Templates
Templates can be used for generating markdowns, and text for emails but the article focuses on HTML templates only. Serving HTML with python is not an easy task due to manual HTML escaping. But Jinja does all the complex work under the hood and provides few syntaxes to create a dynamic template.
Create a folder named templates in the same folder as app.py, i.e. root of the project. And then place all HTML files that is required to be served by flask.
from flask import Flask, render_template
render_template function is available in Flask module.
Let's Make it Interesting
Now it is time to create a small project.
Project goal: A HTML form that collects user info and sends it back to the server. The server then sends a response based on the received data.
from flask import Flask, render_template
app = Flask(__name__)
@app.get('/user-info')
def myName():
return render_template('form.html')
if __name__ == '__main__':
app.run(debug=True)
If the browser sends an HTTP request to the "/user-info" endpoint, a form will be displayed. Instead of using methods parameter, flask route supports functions named after HTTP methods.
Now, to catch and process the data sent through the form, another route is required. Form submits data in POST method, so route has to be same.
from flask import Flask, render_template, request
app = Flask(__name__)
@app.get('/user-info')
def form():
message = request.args.get('message')
return render_template('form.html')
@app.post('/user-info')
def formRequest():
data = request.form
return f'{data}'
if __name__ == '__main__':
app.run(debug=True)
After form submission server returns submitted immutable object data in the form of a string.
Not so Fun Right?
To make it more interesting, following logics can be implemented
Flask throws a 401 error if any of the first name, last name, and a country value is missing
Create a message using form data
Show the message above the form
Let's implement first point
from flask import Flask, render_template, request, abort
app = Flask(__name__)
@app.get('/user-info')
def form():
message = request.args.get('message')
return render_template('form.html')
@app.post('/user-info')
def formRequest():
data = request.form
if not data.get('firstname') or not data.get('lastname') or not data.get('country'):
abort(401)
return f'{data}'
if __name__ == '__main__':
app.run(debug=True)
Let's complete second point,
from flask import Flask, render_template, request, abort
app = Flask(__name__)
@app.get('/user-info')
def form():
message = request.args.get('message')
return render_template('form.html')
@app.post('/user-info')
def formRequest():
data = request.form
if not data.get('firstname') or not data.get('lastname') or not data.get('country'):
abort(401)
message = f"{data.get('firstname')} {data.get('lastname')} lives in {data.get('country')}"
return f'{data}'
if __name__ == '__main__':
app.run(debug=True)
For implementing the third point, two new functions need to be understood. Redirect sends the request to another URL endpoint from the server itself. And url_for is used to get the URL of an endpoint in the form of a string. If the server can redirect the request to get a request of /user-info, the form will automatically open in the browser.
However, the question remains, how to send the message to form function? The most suitable answer is using a query parameter. It is easy to concat a message as a query param in the URL endpoint generated by url_for function.
from flask import Flask, render_template, url_for, request, abort, redirect
app = Flask(__name__)
@app.get('/user-info')
def form():
message = request.args.get('message')
return render_template('form.html', message=message)
@app.post('/user-info')
def formRequest():
data = request.form
if not data.get('firstname') or not data.get('lastname') or not data.get('country'):
abort(401)
message = f"{data.get('firstname')} {data.get('lastname')} lives in {data.get('country')}"
return redirect(f"{url_for('form')}?message={message}")
if __name__ == '__main__':
app.run(debug=True)
In the form function, the message query param is caught using request.args statement. Now, to send the message to the HTML template, an argument can be passed to render_template function. Anything after the template name is passed as a value in keyword arguments, so the same name as the passed param can be used in HTML template to use the value (message in this case).
{% if message %}
<p class="message">{{ message }}</p>
{% endif %}
Recap
The article has covered the basics of Flask, a popular Python web framework. It provided an overview of starting a server, routing HTTP requests, and using HTML templates to generate dynamic web pages. Flask's lightweight and flexible nature allows developers to create web applications quickly and efficiently. By simplifying the process of web development, Flask provides a user-friendly interface for building powerful web applications with minimal effort. For beginners looking to learn Flask and improve their web development skills, this article serves as an excellent starting point. With this newfound knowledge, readers can confidently begin building web applications using Flask.