Understanding Python Flask Applications

Jasai HansdaJasai Hansda
17 min read

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:

  1. 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.
  2. Flexibility:

    • Developers can structure their application however they want, use any extensions they need, and integrate with other libraries or frameworks.
  3. 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).
  4. 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

  1. Install Flask: You can install Flask using pip:

     pip install flask
    
  2. 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 the hello_world function.

    • app.run(debug=True) runs the application in debug mode, allowing for easier debugging during development.

  3. Running the Application: Save the code in a file, for example, app.py, and run it using:

     python app.py
    
  4. 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:

  1. Sign Up for an OpenWeatherMap Account:

    • Visit the OpenWeatherMap website.

    • Sign up for a free account if you don't already have one.

  2. 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.

  3. 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 name

  • appid: the API key

  • units: set to metric 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:

  1. Retrieves weather data from the OpenWeatherMap API when a city name is submitted via a POST request.

  2. Displays the weather data in an index.html template.

  3. 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 }} &deg;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 a lang 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 named style.css, which is located in the static directory. The url_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". The name 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 the weather 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 }} &deg;C</p> displays the temperature in Celsius, with a degree symbol (&deg;C).

        • <p>Humidity: {{ weather.main.humidity }}%</p> displays the humidity percentage.

      • {% elif weather is not none %} checks if the weather 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 class weather-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:

  1. 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'.

  2. 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 a city 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:

  1. Install pytest:

     pip install pytest pytest-watch
    
  2. Run pytest:

     pytest
    
  3. 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 named requirements.txt in the current directory.

  • The > symbol redirects the output of pip freeze to a file.


How API Calls Are Happening

  1. User Input:

    • The user enters the name of a city in the input field and submits the form.
  2. Form Submission:

    • The form data (city name) is sent to the server via a POST request.
  3. Handling POST Request:

    • In app.py, the index function handles the POST request.

    • The city name is extracted from the form data using request.form['city'].

  4. Constructing API Request:

  5. Sending API Request:

    • A GET request is sent to the OpenWeatherMap API using the requests.get function.

    • The response is received in JSON format.

  6. 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 to None.

  7. Rendering Template:

    • The index.html template is rendered with the weather variable containing the weather data.

    • If weather_data is None, a "City not found!" message is displayed.


Role of Flask

Flask is a micro web framework for Python. Its role in this application includes:

  1. Routing:

    • Flask handles routing, mapping URLs to functions (view functions). For example, the root URL / is mapped to the index function.
  2. Request Handling:

    • Flask handles incoming HTTP requests (GET and POST) and provides an easy way to access request data.
  3. Template Rendering:

    • Flask uses Jinja2 for rendering HTML templates. It allows passing data from Python functions to HTML templates.
  4. Serving Static Files:

    • Flask serves static files (e.g., CSS, JavaScript) from the static directory.
  5. 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:

  1. Simple Web Applications:

    • Basic websites and blogs

    • Personal portfolios and resumes

    • Landing pages for products or services

  2. APIs and Microservices:

    • RESTful APIs for frontend-backend communication

    • Microservices in a larger application architecture

  3. Data-Driven Applications:

    • Dashboards for data visualization

    • Analytics and reporting tools

    • CRUD applications (Create, Read, Update, Delete operations on a database)

  4. Authentication Systems:

    • Login and registration systems

    • Social media integration (OAuth)

  5. Real-Time Applications:

    • Chat applications (with Flask-SocketIO)

    • Real-time notifications

  6. E-commerce Platforms:

    • Shopping carts

    • Payment integrations

  7. Content Management Systems:

    • Blog management

    • Article publication platforms

  8. 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.

0
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!