Understanding Python Flask Applications
Python Flask is a lightweight, flexible web framework ideal for building web applications and services. Learn its features, structure, and use cases with examples.
Introduction
Python Flask is a micro web framework for Python, designed to be lightweight and modular. It gives developers the basics to create web applications and services without many rules or requirements.
Flask is known for its simplicity and flexibility, making it an excellent choice for both beginners and experienced developers. With Flask, you can create anything from simple prototypes to complex web applications, leveraging its extensive ecosystem of extensions for added functionality.
Key Features of Flask:
Lightweight and Modular:
- Flask is minimalistic and lets developers make most of the architectural decisions. It's called a "micro" framework because it doesn't have many of the extra features that larger frameworks like Django have.
Flexibility:
- Developers can structure their application however they want, use any extensions they need, and integrate with other libraries or frameworks.
Extensible:
- Flask can be extended with a variety of plugins and extensions, such as those for database integration (e.g., SQLAlchemy), form handling (e.g., WTForms), and authentication (e.g., Flask-Login).
Easy to Learn and Use:
- Flask has a simple and straightforward API, making it accessible for beginners while being powerful enough for advanced users.
Basic Flask Application Structure
Install Flask: You can install Flask using pip:
pip install flask
Create a Flask Application: Here's a basic example of a Flask application:
from flask import Flask app = Flask(__name__) @app.route('/') def hello_world(): return 'Hello, World!' if __name__ == '__main__': app.run(debug=True)
In this example:
Flask(__name__)
creates an instance of the Flask class.@app.route('/')
is a decorator that defines the route for the URL/
, mapping it to thehello_world
function.app.run
(debug=True)
runs the application in debug mode, allowing for easier debugging during development.
Running the Application: Save the code in a file, for example,
app.py
, and run it using:python app.py
Accessing the Application: Open a web browser and navigate to
http://127.0.0.1:5000/
to see the "Hello, World!" message.
Extending Flask
Flask can be extended to handle more complex requirements. For example:
Templates: Use Jinja2 for templating.
Forms: Use WTForms for form handling and validation.
Databases: Use SQLAlchemy for database interactions.
Authentication: Use Flask-Login for managing user sessions and authentication.
Example with Template Rendering
Here's an example that uses Jinja2 templates:
What is a Jinja template:
A Jinja template is simply a text file. Jinja can generate any text-based format (HTML, XML, CSV, LaTeX, etc.). A Jinja template doesn’t need to have a specific extension: .html
, .xml
, or any other extension is just fine.
A template contains variables and/or expressions, which get replaced with values when a template is rendered; and tags, which control the logic of the template. The template syntax is heavily inspired by Django and Python.
Below is a minimal template that illustrates a few basics using the default Jinja configuration:
<!DOCTYPE html>
<html lang="en">
<head>
<title>My Webpage</title>
</head>
<body>
<ul id="navigation">
{% for item in navigation %}
<li><a href="{{ item.href }}">{{ item.caption }}</a></li>
{% endfor %}
</ul>
<h1>My Webpage</h1>
{{ a_variable }}
{# a comment #}
</body>
</html>
The following example shows the default configuration settings. An application developer can change the syntax configuration from {% foo %}
to <% foo %>
, or something similar.
There are a few kinds of delimiters. The default Jinja delimiters are configured as follows:
{% ... %}
for Statements{
{ ... }}
for Expressions to print to the template output{# ... #}
for Comments not included in the template output
Template File Extension:
Any file can be used as a template, no matter its extension. Using a .jinja
extension, like user.html.jinja
, can help some IDEs or editor plugins, but it's not necessary. Auto escaping can be based on the file extension, so think about adding the extra suffix if needed.
A good way to identify templates is to place them in a templates
folder, regardless of their extension. This is a common project layout.
Template Rendering in Flask Application
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def home():
return render_template('index.html')
if __name__ == '__main__':
app.run(debug=True)
In this example, the render_template('index.html')
function is used to render an HTML template.
This template file, named index.html
, is located in the templates
directory of the Flask application.
When the home
function is called, it returns the rendered HTML content of the index.html
file.
This process involves loading the template file, processing any template code within it, and then sending the final HTML output to the client's browser.
The templates
directory is a standard location for storing template files in Flask projects, making it easier to manage and organize the application's structure.
By using the render_template
function, developers can dynamically generate HTML pages based on the data and logic defined in their Flask routes.
A Simple Weather Application
Let's create a simple Flask application that displays weather information for a given location using the OpenWeatherMap API, utilizing Flask's concepts of templates and forms.
Project Setup:
Create the project structure.
Set up a virtual environment and install necessary dependencies (
Flask
,requests
,python-dotenv
).
Project Structure:
weather_app/
├── app.py
├── templates/
│ └── index.html
├── static/
│ └── style.css
├── tests/
│ └── test_app.py
├── .env
└── requirements.txt
Install Necessary Packages:
python3 -m venv venv
source venv/bin/activate # On Windows, use `venv\Scripts\activate`
pip install Flask requests python-dotenv
Note: It is recommended to Activate Your Virtual Environment before installing necessary packages.
Create the .env
File:
To use the OpenWeatherMap API in your project, you need to follow these steps:
Sign Up for an OpenWeatherMap Account:
Visit the OpenWeatherMap website.
Sign up for a free account if you don't already have one.
Generate an API Key:
After logging in, navigate to the API keys section of your account.
Click on "Create API key" to generate a new API key.
Copy the generated API key; you will need it to authenticate your API requests.
Store the API Key Securely:
Create a
.env
file in your project root directory to store the API key securely. The.env
file is not included in version control (e.g., Git) and keeps sensitive information out of your codebase.Add the API key to the
.env
file:API_KEY=your_openweathermap_api_key
Create the Flask Application app.py
:
from flask import Flask, render_template, request
import requests
import os
from dotenv import load_dotenv
# Load environment variables from .env file
load_dotenv()
app = Flask(__name__)
API_KEY = os.getenv('API_KEY')
BASE_URL = "http://api.openweathermap.org/data/2.5/weather"
@app.route('/', methods=['GET', 'POST'])
def index():
weather_data = None
if request.method == 'POST':
city = request.form['city']
params = {
'q': city,
'appid': API_KEY,
'units': 'metric'
}
response = requests.get(BASE_URL, params=params)
weather_data = response.json() if response.status_code == 200 else None
return render_template('index.html', weather=weather_data)
if __name__ == '__main__':
app.run(debug=True)
This is a Flask web application that retrieves and displays weather data from the OpenWeatherMap API. Here's a breakdown of the code:
Importing modules and loading environment variables
The code starts by importing the necessary modules:
Flask
is the web framework used to build the application.render_template
is a function from Flask that renders an HTML template with dynamic data.request
is an object from Flask that represents the current HTTP request.requests
is a library used to make HTTP requests to external APIs.os
is a built-in Python module used to interact with the operating system.dotenv
is a library used to load environment variables from a.env
file.
The load_dotenv()
function is called to load environment variables from a .env
file. This file is not shown in the code, but it typically contains sensitive information like API keys.
Defining the Flask application and API key
The Flask application is created with app = Flask(__name__)
. The API_KEY
variable is set to the value of the API_KEY
environment variable loaded from the .env
file.
Defining the base URL for the OpenWeatherMap API
The BASE_URL
variable is set to the base URL for the OpenWeatherMap API, which is http://api.openweathermap.org/data/2.5/weather
.
Defining the index route
The index
function is defined to handle requests to the root URL (/
) of the application. It accepts both GET and POST requests.
Handling GET requests
When a GET request is made to the root URL, the index
function returns a rendered index.html
template with no weather data.
Handling POST requests
When a POST request is made to the root URL, the index
function retrieves the city
parameter from the request form data. It then constructs a dictionary params
with the following keys:
q
: the city nameappid
: the API keyunits
: set tometric
to retrieve temperature data in Celsius
The requests.get()
function is used to make a GET request to the OpenWeatherMap API with the constructed params
dictionary. If the response status code is 200 (OK), the response JSON data is stored in the weather_data
variable. Otherwise, weather_data
is set to None
.
Finally, the index
function returns a rendered index.html
template with the weather_data
variable passed as an argument.
Running the application
The if __name__ == '__main__':
block is used to ensure that the application is only run when the script is executed directly (i.e., not when it's imported as a module by another script). The app.run
(debug=True)
line starts the Flask development server in debug mode.
In summary, this code creates a Flask application that:
Retrieves weather data from the OpenWeatherMap API when a city name is submitted via a POST request.
Displays the weather data in an
index.html
template.Uses environment variables to store sensitive information like the API key.
Create the HTML Template index.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Weather App</title>
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
</head>
<body>
<h1>Weather App</h1>
<form method="post">
<input type="text" name="city" placeholder="Enter city name">
<button type="submit">Get Weather</button>
</form>
{% if weather %}
<div class="weather-info">
<h2>{{ weather.name }}</h2>
<p>{{ weather.weather[0].description }}</p>
<p>Temperature: {{ weather.main.temp }} °C</p>
<p>Humidity: {{ weather.main.humidity }}%</p>
</div>
{% elif weather is not none %}
<p>City not found!</p>
{% endif %}
</body>
</html>
This is an HTML template for a simple weather application. Here's a breakdown of the code:
Head Section
The HTML document starts with the
<!DOCTYPE html>
declaration, followed by the<html>
element with alang
attribute set to "en" (English).The
<head>
section contains metadata about the document:<meta charset="UTF-8">
specifies the character encoding of the document.<title>Weather App</title>
sets the title of the page, which appears in the browser's title bar and in search engine results.<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
links to an external stylesheet namedstyle.css
, which is located in thestatic
directory. Theurl_for
function is a Jinja2 template function that generates a URL for the stylesheet.
Body Section
The
<body>
section contains the content of the HTML document:<h1>Weather App</h1>
displays a heading with the text "Weather App".<form method="post">
defines a form that submits data to the server using the HTTP POST method.<input type="text" name="city" placeholder="Enter city name">
creates a text input field with a placeholder text "Enter city name". Thename
attribute specifies the name of the input field, which is "city".<button type="submit">Get Weather</button>
creates a submit button with the text "Get Weather".
The next section is a conditional block that displays weather information if it's available:
{% if weather %}
checks if theweather
variable is truthy (i.e., not empty or null).If
weather
is truthy, the following HTML is displayed:<div class="weather-info">
creates a container element with a class of "weather-info".<h2>{{
weather.name
}}</h2>
displays the name of the city in a heading element.<p>{{
weather.weather
[0].description }}</p>
displays the weather description in a paragraph element.<p>Temperature: {{ weather.main.temp }} °C</p>
displays the temperature in Celsius, with a degree symbol (°C
).<p>Humidity: {{ weather.main.humidity }}%</p>
displays the humidity percentage.
{% elif weather is not none %}
checks if theweather
variable is not null or undefined, but is still falsy (i.e., empty).If
weather
is falsy, the following HTML is displayed:<p>City not found!</p>
displays an error message indicating that the city was not found.
In summary, this HTML template displays a form to input a city name, and if the city is found, it displays the weather information (name, description, temperature, and humidity) in a formatted way. If the city is not found, it displays an error message.
Create the CSS File style.css
:
body {
font-family: Arial, sans-serif;
text-align: center;
background-color: #f0f0f0;
}
h1 {
color: #333;
}
form {
margin: 20px 0;
}
.weather-info {
margin-top: 20px;
padding: 10px;
background-color: #fff;
border: 1px solid #ccc;
display: inline-block;
}
This is a CSS (Cascading Style Sheets) code that styles an HTML document. Here's a breakdown of what each section does:
Body Styles
body { ... }
: This is a CSS selector that targets the<body>
element of the HTML document.font-family: Arial, sans-serif;
: Sets the font family of the body text to Arial, with a fallback to a sans-serif font if Arial is not available.text-align: center;
: Centers the text horizontally within the body element.background-color: #f0f0f0;
: Sets the background color of the body element to a light gray color (#f0f0f0).
H1 Styles
h1 { ... }
: This is a CSS selector that targets all<h1>
elements within the HTML document.color: #333;
: Sets the text color of all<h1>
elements to a dark gray color (#333).
Form Styles
form { ... }
: This is a CSS selector that targets all<form>
elements within the HTML document.margin: 20px 0;
: Adds a margin of 20 pixels to the top and bottom of the form element, and no margin to the left and right.
Weather Info Styles
.weather-info { ... }
: This is a CSS selector that targets all elements with the classweather-info
within the HTML document.margin-top: 20px;
: Adds a margin of 20 pixels to the top of the element.padding: 10px;
: Adds padding of 10 pixels to the element, which creates space between the element's content and its border.background-color: #fff;
: Sets the background color of the element to white (#fff).border: 1px solid #ccc;
: Adds a 1-pixel solid border to the element, with a color of light gray (#ccc).display: inline-block;
: Displays the element as an inline block, which allows it to be placed inline with other elements, but also allows it to have a width and height.
In summary, this CSS code styles the HTML document by setting font and text alignment, adding margins and padding, and defining the appearance of <h1>
elements, <form>
elements, and elements with the class weather-info
.
Write Test Cases
Create a tests
directory and add a test_app.py
file:
import unittest
from app import app
class WeatherAppTestCase(unittest.TestCase):
def setUp(self):
self.app = app.test_client()
self.app.testing = True
def test_home_page(self):
response = self.app.get('/')
self.assertEqual(response.status_code, 200)
self.assertIn(b'Weather App', response.data)
def test_weather_post(self):
response = self.app.post('/', data=dict(city='London'))
self.assertEqual(response.status_code, 200)
self.assertIn(b'London', response.data)
if __name__ == '__main__':
unittest.main()
This test file includes a setup method to initialize the Flask test client and two test cases: one to check if the home page loads correctly and another to test the weather functionality by posting a city name.
Here's a breakdown of the code:
Importing necessary modules
The script starts by importing the unittest
module, which provides a framework for writing and running tests. It also imports the app
module, which is assumed to be the Flask application being tested.
Defining the test case class
The WeatherAppTestCase
class is defined, which inherits from unittest.TestCase
. This class will contain the test methods for the weather app.
Setup method
The setUp
method is a special method in unittest
that is called before each test method is executed. In this case, it sets up a test client for the Flask app using app.test_client()
, and sets testing
to True
to enable testing mode.
Test methods
Two test methods are defined:
test_home_page
: This method tests the home page of the weather app.It sends a GET request to the root URL (
'/'
) using the test client.It asserts that the response status code is 200 (OK).
It asserts that the response data contains the string
'Weather App'
.
test_weather_post
: This method tests the weather app's POST endpoint.It sends a POST request to the root URL (
'/'
) with a form data dictionary containing acity
key with value'London'
.It asserts that the response status code is 200 (OK).
It asserts that the response data contains the string
'London'
.
Running the tests
The script ends with a conditional statement that runs the tests if the script is executed directly (i.e., not imported as a module by another script). The unittest.main()
function is called, which discovers and runs all test cases in the script.
python -m unittest discover -s tests
Running Tests Continuously
For continuous testing, you can use tools like pytest
along with plugins like pytest-watch
. To install and use pytest
:
Install
pytest
:pip install pytest pytest-watch
Run
pytest
:pytest
Run
pytest
in Watch Mode:ptw
This will automatically run your tests whenever you make changes to your code or tests.
Run the Flask Application:
flask run
Navigate to http://127.0.0.1:5000/
to use the application. This URL points to the local server where the Flask application is running.
Create a Requirements File:
Generate a requirements.txt
file with the necessary dependencies:
pip freeze > requirements.txt
Your requirements.txt
file should look something like this:
click==8.1.3
Flask==2.2.3
itsdangerous==2.1.2
Jinja2==3.1.2
MarkupSafe==2.1.2
requests==2.28.1
python-dotenv==1.0.0
Werkzeug==2.2.3
This command should be used after running your application successfully so that you can share this requirements file along with your Python application.
This command saves the output of
pip freeze
, which lists all installed packages and their versions, into a file namedrequirements.txt
in the current directory.The
>
symbol redirects the output ofpip freeze
to a file.
How API Calls Are Happening
User Input:
- The user enters the name of a city in the input field and submits the form.
Form Submission:
- The form data (city name) is sent to the server via a POST request.
Handling POST Request:
In
app.py
, theindex
function handles the POST request.The city name is extracted from the form data using
request.form['city']
.
Constructing API Request:
The base URL for the OpenWeatherMap API is
http://api.openweathermap.org/data/2.5/weather
.Parameters for the API request include the city name, API key, and units (metric).
Sending API Request:
A GET request is sent to the OpenWeatherMap API using the
requests.get
function.The response is received in JSON format.
Processing API Response:
If the response status code is 200, the JSON data is extracted and stored in
weather_data
.If the response status code is not 200,
weather_data
is set toNone
.
Rendering Template:
The
index.html
template is rendered with theweather
variable containing the weather data.If
weather_data
isNone
, a "City not found!" message is displayed.
Role of Flask
Flask is a micro web framework for Python. Its role in this application includes:
Routing:
- Flask handles routing, mapping URLs to functions (view functions). For example, the root URL
/
is mapped to theindex
function.
- Flask handles routing, mapping URLs to functions (view functions). For example, the root URL
Request Handling:
- Flask handles incoming HTTP requests (GET and POST) and provides an easy way to access request data.
Template Rendering:
- Flask uses Jinja2 for rendering HTML templates. It allows passing data from Python functions to HTML templates.
Serving Static Files:
- Flask serves static files (e.g., CSS, JavaScript) from the
static
directory.
- Flask serves static files (e.g., CSS, JavaScript) from the
Development Server:
- Flask includes a built-in development server for running the application locally during development.
Summary
Flask: Manages routing, request handling, template rendering, and static file serving.
API Calls: The application sends a GET request to the OpenWeatherMap API with the city name and API key, processes the response, and displays the weather information.
Template Rendering: The
index.html
template is rendered with the weather data and displayed to the user.
By following these steps and understanding the role of Flask, you can build a simple yet functional weather application that interacts with an external API to provide real-time weather information.
Types of Applications
Python Flask is a micro web framework that's lightweight yet powerful, making it suitable for a wide range of applications. Here's a breakdown of the types of applications you can build with Flask:
Simple Web Applications:
Basic websites and blogs
Personal portfolios and resumes
Landing pages for products or services
APIs and Microservices:
RESTful APIs for frontend-backend communication
Microservices in a larger application architecture
Data-Driven Applications:
Dashboards for data visualization
Analytics and reporting tools
CRUD applications (Create, Read, Update, Delete operations on a database)
Authentication Systems:
Login and registration systems
Social media integration (OAuth)
Real-Time Applications:
Chat applications (with Flask-SocketIO)
Real-time notifications
E-commerce Platforms:
Shopping carts
Payment integrations
Content Management Systems:
Blog management
Article publication platforms
Machine Learning and AI Integration:
Serving machine learning models
Interactive data analysis tools
Conclusion
Flask is a versatile and powerful framework that can be used to build a wide range of web applications, from simple prototypes to complex applications. Its minimalistic approach provides developers with the flexibility to create applications tailored to their specific needs.
Subscribe to my newsletter
Read articles from Jasai Hansda directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Jasai Hansda
Jasai Hansda
Software Engineer (2 years) | In-transition to DevOps. Passionate about building and deploying software efficiently. Eager to leverage my development background in the DevOps and cloud computing world. Open to new opportunities!