Course topics

By WebNest Studio

Python Tutorial

Flask Basics

Flask is Python's classic micro-framework. It gives you routing, request and response handling, HTML templating with Jinja2 and a development server — and leaves everything else (database, forms, authentication) to extensions you choose. It is simple, flexible and still used by a huge number of applications, so every Python web developer should be able to read and write Flask code.

This lesson covers routes and URL variables, handling requests and returning JSON, templates, blueprints and testing — and compares Flask with FastAPI so you can choose between them.

Core Concepts

Create an app with app = Flask(__name__) and map URLs to view functions with @app.route("/path") or the shortcuts @app.get and @app.post. URL variables use converters: /books/<int:book_id>. The global request object holds request.args (query string), request.form, request.get_json() and headers. Return a string, a dict (converted to JSON), a (body, status) tuple, or use jsonify(), redirect() and abort(404). Run with flask --app app run --debug.

Templates and Blueprints

render_template("page.html", **context) renders Jinja2 templates from the templates/ folder: {{ variable }} prints (auto-escaped against XSS), {% for %} and {% if %} control output, and {% extends "base.html" %} shares layouts. Blueprints group related routes, like FastAPI's APIRouter, and are registered with app.register_blueprint(bp, url_prefix="/api").

Flask or FastAPI?

  • FastAPI — JSON APIs, automatic validation and docs from type hints, async support, high performance.
  • Flask — server-rendered websites and small services, a mature extension ecosystem (Flask-SQLAlchemy, Flask-Login, Flask-WTF), very little magic.
  • Django — large, full-featured websites needing an admin panel, ORM, auth and more out of the box.
  • Flask validates nothing by itself; add Pydantic or marshmallow when you need input validation.

Examples

Routes, URL variables, query strings and JSON

Python
# app.py      pip install flask      run: flask --app app run --debug
from flask import Flask, abort, jsonify, request

app = Flask(__name__)
books = {1: {"title": "Python Basics", "price": 450}, 2: {"title": "Deep Python", "price": 899}}

@app.get("/")
def home():
    return "<h1>Webnest Books</h1>"

@app.get("/books")
def list_books():
    max_price = request.args.get("max_price", type=int)      # ?max_price=500
    result = [{"id": i, **b} for i, b in books.items() if max_price is None or b["price"] <= max_price]
    return jsonify(result)

@app.get("/books/<int:book_id>")
def get_book(book_id):
    if book_id not in books:
        abort(404, description="Book not found")
    return books[book_id]                                   # dicts become JSON

@app.post("/books")
def add_book():
    data = request.get_json()
    if not data or "title" not in data:
        return {"error": "title is required"}, 400
    book_id = max(books) + 1
    books[book_id] = {"title": data["title"], "price": data.get("price", 0)}
    return {"id": book_id, **books[book_id]}, 201

client = app.test_client()                                  # Flask's built-in test client
print(client.get("/").data.decode())
print(client.get("/books?max_price=500").get_json())
print(client.get("/books/2").get_json(), client.get("/books/9").status_code)
r = client.post("/books", json={"title": "SQL Basics", "price": 350})
print(r.status_code, r.get_json())
print(client.post("/books", json={"price": 10}).get_json())
Output
<h1>Webnest Books</h1>
[{'id': 1, 'price': 450, 'title': 'Python Basics'}]
{'price': 899, 'title': 'Deep Python'} 404
201 {'id': 3, 'price': 350, 'title': 'SQL Basics'}
{'error': 'title is required'}

Jinja2 templates, blueprints and error handlers

Python
from flask import Blueprint, Flask, render_template_string

# templates/books.html (inlined here with render_template_string)
PAGE = """<h2>{{ heading }}</h2>
<ul>{% for book in books %}
  <li>{{ book.title }}{% if book.price < 500 %} (budget){% endif %}</li>{% endfor %}
</ul>"""

shop = Blueprint("shop", __name__)
BOOKS = [{"title": "Python Basics", "price": 450}, {"title": "<script>alert(1)</script>", "price": 999}]

@shop.get("/books")
def books_page():
    return render_template_string(PAGE, heading="All books", books=BOOKS)

app = Flask(__name__)
app.register_blueprint(shop, url_prefix="/shop")

@app.errorhandler(404)
def not_found(error):
    return {"error": "not found"}, 404

client = app.test_client()
print(client.get("/shop/books").data.decode())               # note the escaped <script>
print(client.get("/nowhere").get_json())
Output
<h2>All books</h2>
<ul>
  <li>Python Basics (budget)</li>
  <li>&lt;script&gt;alert(1)&lt;/script&gt;</li>
</ul>
{'error': 'not found'}

Common Mistakes

  • Running the development server (flask run --debug) in production — use Gunicorn or Waitress.
  • Trusting request.get_json() data without validating it.
  • Marking user content as safe in templates, which disables escaping and allows XSS.
  • Keeping all routes in one file instead of using blueprints.
  • Forgetting to set a strong SECRET_KEY when using sessions.

Key Points to Remember

  • Flask maps routes to view functions; request holds args, form, JSON and headers.
  • Return strings, dicts, (body, status) tuples, or use jsonify, redirect and abort.
  • Jinja2 templates auto-escape output and support loops, conditions and inheritance.
  • Blueprints organise routes; app.test_client() tests them.
  • Choose FastAPI for typed JSON APIs, Flask for flexible small apps and server-rendered pages, Django for full-featured sites.

Practice the examples

Change an input, predict the result, then compare it with the output. Explain why the result changes.

Use your local project environment for these examples. Codelab currently runs Python and HTML/CSS/JavaScript; framework examples may need project dependencies.