Python Flask
Learn how to build web applications and APIs with Flask, a lightweight and flexible Python web framework used for everything from small applications to production services.
Learning Objectives
Flask introduces an important new stage in your Python journey: building applications that communicate through the web.
By the end of this lesson, you will be able to:
- Understand what Flask is and why it is useful.
- Install Flask inside a Python environment.
- Create your first Flask application.
- Understand routes and URLs.
- Work with HTTP methods.
- Use dynamic route parameters.
- Read query parameters and form data.
- Render HTML templates using Jinja.
- Serve CSS, JavaScript, and other static files.
- Return text and JSON responses.
- Use redirects, sessions, and error handlers.
- Organize a small Flask project professionally.
What Is Flask?
Flask is a lightweight web framework written in Python.
It provides the fundamental tools required to receive HTTP requests, execute Python code, and return responses to users.
Simple idea
A Flask application can receive a request such as
/products, execute Python logic, and
return HTML, JSON, or another response.
Flask is often described as a microframework because its core is intentionally small and flexible.
You can add additional libraries and components depending on what your application needs.
What Is a Web Framework?
A web framework provides tools and conventions that make building web applications easier.
Without a framework, you would need to handle many low-level web details yourself.
Flask helps with:
- Receiving HTTP requests
- Routing URLs to Python functions
- Returning HTTP responses
- Rendering HTML templates
- Handling forms
- Managing sessions
- Returning JSON
- Handling errors
Flask does not force you into one large application architecture. This flexibility makes it useful for learning as well as building many types of web applications.
Installing Flask
Flask should normally be installed inside a virtual environment for your project.
python -m pip install flask
Verify that Flask is available:
python -m pip show flask
Recommended practice
Use a virtual environment so that your Flask project's dependencies remain isolated from other Python projects.
Your First Flask Application
Create a file named app.py.
from flask import Flask
app = Flask(__name__)
@app.route("/")
def home():
return "Hello, PyLearn!"
if __name__ == "__main__":
app.run(debug=True)
Understanding the Code
-
Flaskis imported from the Flask package. -
Flask(__name__)creates the application. -
@app.route("/")connects the root URL to thehome()function. - The function returns the response.
-
app.run()starts the development server.
Your first web route
When you visit the application's root URL, Flask
executes home() and sends its returned
text to the browser.
Running the Development Server
From the project directory, run:
python app.py
Flask will start a local development server.
You will normally see an address similar to:
http://127.0.0.1:5000
Development server warning
Flask's built-in development server is intended for development and testing, not as the production server for a deployed application.
The debug=True option enables useful development
features such as automatic reloading and detailed error
information.
Routes
A route maps a URL to a Python function.
@app.route("/")
def home():
return "Home Page"
@app.route("/about")
def about():
return "About Page"
@app.route("/contact")
def contact():
return "Contact Page"
Now the application has three different URLs.
/→ Home/about→ About/contact→ Contact
Routes are one of the central concepts of Flask.
HTTP Methods
HTTP defines several methods that describe what the client wants to do.
- GET — retrieve information.
- POST — submit or create information.
- PUT — replace an existing resource.
- PATCH — partially update a resource.
- DELETE — remove a resource.
GET Route
@app.route("/products", methods=["GET"])
def products():
return "Product List"
POST Route
@app.route("/products", methods=["POST"])
def create_product():
return "Product Created"
A single route can also support multiple methods.
@app.route(
"/products",
methods=["GET", "POST"]
)
def product_handler():
if request.method == "GET":
return "Get Products"
return "Create Product"
When using request, remember to import it:
from flask import Flask, request
Route Parameters
Dynamic route parameters allow part of the URL to become input for your Python function.
@app.route("/user/<username>")
def user_profile(username):
return f"Welcome, {username}!"
Visiting a URL such as:
/user/Ali
passes "Ali" into the function.
Integer Parameters
@app.route("/product/<int:product_id>")
def product(product_id):
return f"Product ID: {product_id}"
Common converters
<string:name><int:id><float:price><path:subpath>
Query Parameters
Query parameters appear after a question mark in a URL.
/products?category=phones&page=2
Flask provides the request object for reading
them.
from flask import Flask, request
@app.route("/products")
def products():
category = request.args.get("category")
page = request.args.get("page", default=1, type=int)
return f"Category: {category}, Page: {page}"
request.args.get() is useful because you can
provide a default value when a parameter is missing.
Returning Responses
Flask routes can return different kinds of responses.
Text
@app.route("/hello")
def hello():
return "Hello World!"
HTML
@app.route("/welcome")
def welcome():
return "<h1>Welcome to PyLearn</h1>"
For real applications, HTML should normally be placed in templates rather than embedded directly in Python strings.
HTML Templates
Flask uses a templates directory for HTML
templates.
A simple project may look like:
project/
│
├── app.py
│
└── templates/
└── index.html
Create index.html:
<!DOCTYPE html>
<html>
<head>
<title>PyLearn Flask App</title>
</head>
<body>
<h1>Welcome to PyLearn</h1>
</body>
</html>
Render it from Flask:
from flask import Flask, render_template
app = Flask(__name__)
@app.route("/")
def home():
return render_template("index.html")
Jinja Templates
Flask uses the Jinja template engine to generate dynamic HTML.
Jinja allows Python-generated values to be inserted into HTML templates.
Variable Example
<h1>Hello, {{ name }}!</h1>
Flask:
@app.route("/<name>")
def hello(name):
return render_template(
"hello.html",
name=name
)
Conditional Template Logic
{% if logged_in %}
<p>Welcome back!</p>
{% else %}
<p>Please log in.</p>
{% endif %}
Loops
<ul>
{% for product in products %}
<li>
{{ product }}
</li>
{% endfor %}
</ul>
Important distinction
Jinja syntax resembles Python in some places, but template code is not simply Python pasted into HTML. Jinja has its own template syntax.
Passing Data to Templates
Flask can pass Python variables into an HTML template.
@app.route("/products")
def products():
products = [
"Laptop",
"Phone",
"Tablet"
]
return render_template(
"products.html",
products=products
)
The template can then loop through the data.
<h1>Products</h1>
<ul>
{% for product in products %}
<li>{{ product }}</li>
{% endfor %}
</ul>
Static Files
Web applications commonly contain CSS, JavaScript, images, and other files that do not need to be generated dynamically.
Flask conventionally stores these in a static
directory.
project/
│
├── app.py
│
├── templates/
│ └── index.html
│
└── static/
├── css/
│ └── style.css
│
└── js/
└── script.js
In the template:
<link
rel="stylesheet"
href="{{ url_for('static', filename='css/style.css') }}"
>
Flask's url_for() function generates the
appropriate URL for the static file.
HTML Forms
Forms allow users to send information to a web application.
<form method="POST">
<input
type="text"
name="username"
placeholder="Enter your name"
>
<button type="submit">
Submit
</button>
</form>
Flask can receive the submitted form data.
from flask import Flask, request
@app.route("/login", methods=["GET", "POST"])
def login():
if request.method == "POST":
username = request.form.get("username")
return f"Hello, {username}"
return render_template("login.html")
Security matters
Real authentication systems require password hashing, secure sessions, CSRF protection, validation, and other security measures. Never store plaintext passwords.
The request Object
Flask's request object contains information
about the incoming HTTP request.
from flask import request
Useful request attributes
-
request.method— HTTP method. -
request.args— query parameters. -
request.form— submitted form data. -
request.json— JSON request body. -
request.files— uploaded files. -
request.headers— HTTP headers.
Understanding the request object becomes particularly important when building APIs.
Redirects & URLs
A redirect tells the browser to visit another URL.
from flask import redirect, url_for
@app.route("/old")
def old_page():
return redirect(url_for("home"))
url_for() is useful because it generates URLs
from endpoint names instead of manually constructing paths.
Why use url_for()?
If your URL structure changes later, generating URLs through Flask is safer and easier to maintain than hard-coding paths throughout your application.
Returning JSON
Flask can return JSON data, which is essential when building APIs.
from flask import jsonify
@app.route("/api/products")
def api_products():
products = [
{
"id": 1,
"name": "Laptop",
"price": 1200
},
{
"id": 2,
"name": "Phone",
"price": 800
}
]
return jsonify(products)
A client such as JavaScript, a mobile application, or another Python program can consume this JSON.
Flask and APIs
This is one reason Flask is useful beyond traditional HTML websites. It can also serve as a backend API.
Error Handling
Web applications should handle common HTTP errors gracefully.
from flask import Flask
@app.errorhandler(404)
def page_not_found(error):
return "Page not found", 404
The second value in the returned tuple is the HTTP status code.
Example
return {
"error": "Product not found"
}, 404
Returning the correct status code allows clients to understand what happened.
- 200 — successful request.
- 201 — resource created.
- 400 — bad request.
- 401 — authentication required.
- 403 — forbidden.
- 404 — resource not found.
- 500 — server-side error.
Sessions
Sessions allow an application to remember information between requests for a user.
from flask import Flask, session
app = Flask(__name__)
app.secret_key = "change-this-in-a-real-project"
@app.route("/login")
def login():
session["username"] = "Ali"
return "Logged in"
The stored value can later be retrieved:
@app.route("/profile")
def profile():
username = session.get("username")
return f"User: {username}"
Never expose secret keys
In real applications, secret keys should not be hard-coded into publicly shared source code. Use environment variables or a secure configuration system.
Professional Flask Project Structure
As applications grow, keeping everything in one Python file becomes difficult.
A small Flask application may start like this:
my_flask_app/
│
├── app.py
│
├── templates/
│ ├── index.html
│ ├── products.html
│ └── login.html
│
└── static/
├── css/
│ └── style.css
│
├── js/
│ └── script.js
│
└── images/
Larger Flask applications can later use application factories, blueprints, configuration modules, services, database layers, and separate packages.
Start simple, scale deliberately
You do not need a complicated architecture for a small project. Good structure should grow as the application's complexity grows.
Flask & Databases
Real web applications frequently need persistent data such as users, products, orders, payments, and inventory.
Flask itself does not force you to use one particular database.
Common choices
- SQLite
- PostgreSQL
- MySQL
- Other database systems through appropriate libraries
You have already learned SQLite earlier in this course. That knowledge can now be connected with Flask to create database-driven web applications.
Conceptual Flow
Browser
↓
Flask Route
↓
Python Logic
↓
Database
↓
Python Result
↓
HTML / JSON Response
↓
Browser
Practical Example — Product Catalog
Let's create a small Flask application that displays an ecommerce product catalog.
app.py
from flask import Flask, render_template
app = Flask(__name__)
products = [
{
"name": "Laptop",
"price": 1200
},
{
"name": "Phone",
"price": 800
},
{
"name": "Tablet",
"price": 500
}
]
@app.route("/")
def home():
return render_template(
"products.html",
products=products
)
if __name__ == "__main__":
app.run(debug=True)
templates/products.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Product Catalog</title>
</head>
<body>
<h1>Product Catalog</h1>
<ul>
{% for product in products %}
<li>
<strong>
{{ product.name }}
</strong>
- ${{ product.price }}
</li>
{% endfor %}
</ul>
</body>
</html>
Flask sends the Python list to Jinja, and Jinja generates the HTML for each product.
Ecommerce connection
This same concept can eventually become a real ecommerce backend where products are loaded from a database, customers submit orders, and APIs communicate with frontend applications.
Common Flask Mistakes
Mistake 1 — Wrong project structure
Flask expects templates and static files in their conventional locations unless you explicitly configure different paths.
Mistake 2 — Forgetting the route decorator
A Python function does not automatically become a web endpoint. It needs to be connected to a route.
Mistake 3 — Wrong HTTP method
A route designed for POST requests cannot be used as though it only accepts GET requests.
Mistake 4 — Hard-coding URLs everywhere
Use url_for() where appropriate instead
of manually repeating URL paths throughout templates.
Mistake 5 — Exposing secrets
API keys, passwords, secret keys, and database credentials should not be committed directly into source code.
Mistake 6 — Using debug mode in production
Debug mode is useful during development but should not be treated as a production deployment configuration.
Mistake 7 — Trusting user input
User-submitted data must be validated and handled safely. Never assume browser input is trustworthy.
Practice Exercises
Exercise 1 — Hello Flask
Create a Flask application with a root route that returns a welcome message.
Exercise 2 — Multiple Pages
Create /, /about, and
/contact routes.
Exercise 3 — Dynamic Route
Create a route such as
/student/<name> and display the
student's name.
Exercise 4 — Query Parameters
Create a product search route that reads a
keyword query parameter.
Exercise 5 — HTML Template
Create an HTML template and render it using
render_template().
Exercise 6 — Product List
Pass a Python list of products into a Jinja template and display them using a loop.
Mini Challenge — Flask Ecommerce Catalog
Build a small Flask ecommerce catalog that demonstrates the concepts from this lesson.
Your Tasks
- Create a Flask application.
-
Create a
/route for the homepage. -
Create a
/productsroute. - Store at least five products in a Python list.
- Display products using a Jinja template.
- Include product name, price, and category.
-
Create a dynamic route:
/product/<int:id>. - Create a search route using a query parameter.
-
Add CSS using the
staticdirectory. -
Add an API endpoint such as
/api/products. - Return the products from the API as JSON.
- Add a custom 404 page or error response.
Suggested Starting Point
from flask import (
Flask,
render_template,
request,
jsonify
)
app = Flask(__name__)
products = [
{
"id": 1,
"name": "Laptop",
"price": 1200,
"category": "Electronics"
},
{
"id": 2,
"name": "Phone",
"price": 800,
"category": "Electronics"
},
{
"id": 3,
"name": "Tablet",
"price": 500,
"category": "Electronics"
},
{
"id": 4,
"name": "Backpack",
"price": 80,
"category": "Accessories"
},
{
"id": 5,
"name": "Headphones",
"price": 120,
"category": "Accessories"
}
]
@app.route("/")
def home():
return render_template(
"index.html"
)
@app.route("/products")
def product_list():
keyword = request.args.get(
"keyword"
)
filtered = products
if keyword:
filtered = [
product
for product in products
if keyword.lower()
in product["name"].lower()
]
return render_template(
"products.html",
products=filtered
)
@app.route("/api/products")
def api_products():
return jsonify(products)
if __name__ == "__main__":
app.run(debug=True)
Challenge goal
Try to make your application feel like a real small ecommerce backend rather than simply completing isolated Flask exercises.
Knowledge Check
Test your understanding of Flask before moving forward.
1. What is Flask?
2. Which decorator connects a function to a URL?
3. Which command installs Flask?
4. What is render_template() used for?
5. Which template engine does Flask commonly use?
6. Where are Flask HTML templates conventionally stored?
7. Which object provides information about an incoming request?
8. Which Flask function is commonly used to return JSON?
9. What is url_for() useful for?
10. Is Flask's built-in development server intended as a production server?
Lesson Summary
Flask brings Python into web development. You can now connect Python functions to URLs, receive user requests, generate HTML, and return JSON.
Key concepts to remember
- Flask is a lightweight Python web framework.
-
Flask(__name__)creates the application. -
@app.route()connects URLs to functions. - HTTP methods describe how clients interact with resources.
- Route parameters make URLs dynamic.
-
Query parameters can be accessed through
request.args. -
Form data can be accessed through
request.form. -
render_template()renders HTML templates. - Jinja allows dynamic data and logic inside templates.
-
Static files belong in the
staticdirectory. -
jsonify()is useful for API responses. -
url_for()generates endpoint URLs. - Sessions can maintain information between requests.
- Flask applications can connect to databases such as SQLite.
What's next?
Flask introduced you to synchronous web applications. In the next lesson, you will learn FastAPI, a modern Python framework particularly well suited to APIs, type hints, and asynchronous applications.
Ready for the next lesson?
Mark this lesson complete when you can create a basic Flask application, define routes, render templates, and return JSON.