Python Flask Reference

Python Flask Reference

Flask is a lightweight and versatile web framework for Python. It's known for its simplicity and flexibility, making it an excellent choice for both beginners and experienced developers. Whether you're building a small personal project or a large-scale application, Flask provides the tools you need without getting in your way.

Getting Started with Flask

To begin using Flask, you first need to install it. You can do this using pip, Python's package manager. Open your terminal or command prompt and run the following command.

pip install Flask

Once installed, you can create a basic Flask application with just a few lines of code. Here's a simple "Hello, World!" example to get you started.

from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hello, World!'

if __name__ == '__main__':
    app.run(debug=True)

Save this code in a file named app.py and run it using python app.py. Open your web browser and go to http://127.0.0.1:5000/ to see your application in action. You should see the message "Hello, World!" displayed on the page.

This basic example demonstrates the core concept of Flask: defining routes and associating them with functions. The @app.route('/') decorator tells Flask that the hello_world function should be called when someone visits the root URL of your site.

Flask's simplicity doesn't mean it lacks power. You can easily extend your application with additional routes, templates, and database connections. Let's explore some of these features in more detail.

One of Flask's strengths is its extensive documentation and active community. If you ever get stuck or need to learn more about a specific feature, the official Flask documentation is an excellent resource. You can find it at https://flask.palletsprojects.com/.

Common Flask Decorators Description
@app.route() Defines a URL route and associates it with a function
@app.before_request Registers a function to run before each request
@app.after_request Registers a function to run after each request
@app.errorhandler Handles specific HTTP error codes
  • Start with simple routes
  • Use debug mode during development
  • Learn about request handling
  • Explore template rendering
  • Practice with form processing

Routing is fundamental to any web framework, and Flask makes it incredibly straightforward. You can define routes for different URLs and HTTP methods, allowing you to create complex web applications with clean, organized code.

Working with Routes and Views

Routes are the backbone of any Flask application. They define how your application responds to client requests to particular URLs. Flask uses decorators to make route definition simple and intuitive.

Beyond basic routes, you can create dynamic routes that accept variable parts in the URL. This is useful for creating pages that display content based on parameters passed in the URL.

@app.route('/user/<username>')
def show_user_profile(username):
    return f'User {username}'

@app.route('/post/<int:post_id>')
def show_post(post_id):
    return f'Post {post_id}'

In these examples, <username> captures a string from the URL, while <int:post_id> ensures that the captured value is converted to an integer. Flask supports several converter types including string, int, float, path, and uuid.

You can also handle different HTTP methods on the same route. By default, routes only answer to GET requests, but you can specify other methods using the methods parameter.

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        return do_the_login()
    else:
        return show_the_login_form()

This approach allows you to handle both displaying a login form (GET) and processing form submission (POST) within the same function.

Template rendering is another powerful feature of Flask. While you can return plain strings from your view functions, most real-world applications need to return HTML pages. Flask integrates with Jinja2, a powerful templating engine that lets you create dynamic HTML content.

from flask import render_template

@app.route('/hello/')
@app.route('/hello/<name>')
def hello(name=None):
    return render_template('hello.html', name=name)

In this example, the render_template function looks for a file called hello.html in the templates directory and passes the name variable to it. The template can then use this variable to generate dynamic content.

When working with forms, Flask provides the request object to access form data. For more complex form handling, you might want to use the Flask-WTF extension, which provides integration with WTForms and includes CSRF protection.

from flask import request

@app.route('/login', methods=['POST'])
def login():
    username = request.form['username']
    password = request.form['password']
    # Process login logic

Remember to always validate and sanitize user input to protect your application from security vulnerabilities. Flask doesn't automatically handle input validation, so you need to implement this yourself or use extensions like Flask-WTF.

Common HTTP Methods Typical Use Cases
GET Retrieving data, displaying pages
POST Submitting forms, creating resources
PUT Updating existing resources
DELETE Removing resources
PATCH Partial updates to resources
  • Use variable rules for dynamic content
  • Specify HTTP methods explicitly
  • Validate all user input
  • Use templates for HTML responses
  • Implement proper error handling

Request handling in Flask is managed through the request object, which gives you access to incoming request data. This includes form data, URL parameters, cookies, and files. Understanding how to properly handle requests is crucial for building secure and efficient web applications.

Templates and Static Files

Flask uses Jinja2 as its template engine, which provides a powerful way to create dynamic HTML pages. Templates allow you to separate your application logic from your presentation layer, making your code more organized and maintainable.

To use templates, create a templates directory in your project folder. Flask will automatically look for templates in this directory. Here's a simple example of a Jinja2 template:

<!DOCTYPE html>
<html>
<head>
    <title>My Webpage</title>
</head>
<body>
    <h1>Hello, {{ name }}!</h1>
    {% if user %}
        <p>Welcome back, {{ user.username }}!</p>
    {% else %}
        <p>Please log in.</p>
    {% endif %}
</body>
</html>

Jinja2 templates support variables (using {{ }}), control structures (using {% %}), and template inheritance. Template inheritance is particularly useful for creating a consistent layout across your application.

You can create a base template that defines the overall structure of your pages:

<!-- base.html -->
<!DOCTYPE html>
<html>
<head>
    <title>{% block title %}{% endblock %} - My Flask App</title>
</head>
<body>
    <div id="content">
        {% block content %}{% endblock %}
    </div>
</body>
</html>

Then extend this base template in your individual pages:

<!-- hello.html -->
{% extends "base.html" %}

{% block title %}Hello{% endblock %}

{% block content %}
    <h1>Hello, {{ name }}!</h1>
{% endblock %}

Static files like CSS, JavaScript, and images are handled through Flask's built-in support for static file serving. Create a static directory in your project folder, and Flask will serve files from this directory at the /static URL path.

<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">

The url_for function is used to generate URLs for static files and routes. It's the recommended way to create URLs in your templates because it makes your application more maintainable - if you change a route name, you only need to update it in one place.

Template filters are another powerful feature of Jinja2. They allow you to modify variables in your templates. Flask includes several built-in filters, and you can also create your own custom filters.

<p>{{ user.bio|truncate(100) }}</p>
<p>Joined: {{ user.join_date|datetimeformat }}</p>

For more complex template logic, you can use macros, which are similar to functions in programming languages. They allow you to define reusable chunks of template code.

{% macro render_comment(comment) %}
    <div class="comment">
        <h4>{{ comment.author }}</h4>
        <p>{{ comment.text }}</p>
    </div>
{% endmacro %}

Then you can use this macro throughout your templates:

{{ render_comment(comment) }}

When working with forms in templates, it's important to include CSRF protection. If you're using Flask-WTF, it automatically handles CSRF protection, and you can include the CSRF token in your forms like this:

<form method="post">
    {{ form.csrf_token }}
    {{ form.username.label }} {{ form.username }}
    {{ form.password.label }} {{ form.password }}
    <input type="submit" value="Login">
</form>

Remember that while templates are powerful, you should keep business logic out of them as much as possible. Templates should focus on presentation, while your Python code handles the application logic.

Common Jinja2 Filters Description
safe Mark value as safe, preventing auto-escaping
capitalize Capitalize the first character
lower Convert to lowercase
upper Convert to uppercase
title Capitalize each word
trim Remove leading and trailing whitespace
striptags Remove HTML tags
  • Use template inheritance for consistent layouts
  • Keep business logic out of templates
  • Utilize built-in Jinja2 filters
  • Create custom filters for repeated transformations
  • Always escape variables unless they're safe HTML

Template context processors allow you to make variables available to all templates automatically. This is useful for data that needs to be available across your entire application, such as the current user or site configuration.

Working with Forms and Data

Handling forms is a common requirement for web applications, and Flask provides several ways to work with form data. The most basic approach uses the request object to access form data submitted by users.

When working with forms, it's crucial to consider security. Always validate and sanitize user input to prevent common vulnerabilities like SQL injection and cross-site scripting (XSS). For more robust form handling, consider using the Flask-WTF extension, which provides integration with WTForms.

Here's a basic example of form handling without extensions:

from flask import request, redirect, url_for

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        username = request.form['username']
        password = request.form['password']
        # Validate credentials
        if validate_credentials(username, password):
            return redirect(url_for('dashboard'))
        else:
            return 'Invalid credentials'
    return '''
        <form method="post">
            <input type="text" name="username">
            <input type="password" name="password">
            <input type="submit" value="Login">
        </form>
    '''

For more complex forms, Flask-WTF provides a better solution. It includes CSRF protection, form validation, and file upload handling. Here's how you might use it:

from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField
from wtforms.validators import DataRequired

class LoginForm(FlaskForm):
    username = StringField('Username', validators=[DataRequired()])
    password = PasswordField('Password', validators=[DataRequired()])

@app.route('/login', methods=['GET', 'POST'])
def login():
    form = LoginForm()
    if form.validate_on_submit():
        # Process login
        return redirect(url_for('dashboard'))
    return render_template('login.html', form=form)

File uploads are another common requirement. Flask makes it easy to handle file uploads through the request.files object. Always validate file uploads to ensure they meet your requirements for type and size.

from flask import request
from werkzeug.utils import secure_filename

@app.route('/upload', methods=['POST'])
def upload_file():
    if 'file' not in request.files:
        return 'No file part'
    file = request.files['file']
    if file.filename == '':
        return 'No selected file'
    if file and allowed_file(file.filename):
        filename = secure_filename(file.filename)
        file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
        return 'File uploaded successfully'

Database integration is often necessary for web applications. Flask doesn't include a database abstraction layer by design, but it works well with several database libraries. SQLAlchemy is a popular choice for relational databases, and Flask-SQLAlchemy provides Flask integration.

For simple applications, you might use SQLite with Flask:

import sqlite3
from flask import g

def get_db():
    if 'db' not in g:
        g.db = sqlite3.connect(
            'database.sqlite',
            detect_types=sqlite3.PARSE_DECLTYPES
        )
        g.db.row_factory = sqlite3.Row
    return g.db

@app.teardown_appcontext
def close_db(e=None):
    db = g.pop('db', None)
    if db is not None:
        db.close()

For more complex applications, consider using Flask-SQLAlchemy:

from flask_sqlalchemy import SQLAlchemy

app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///database.sqlite'
db = SQLAlchemy(app)

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=False)

    def __repr__(self):
        return f'<User {self.username}>'

Session management is important for maintaining state across requests. Flask provides client-side sessions that are cryptographically signed to prevent tampering. You can use sessions to store user-specific data between requests.

from flask import session

@app.route('/')
def index():
    if 'username' in session:
        return f'Logged in as {session["username"]}'
    return 'You are not logged in'

@app.route('/login', methods=['POST'])
def login():
    session['username'] = request.form['username']
    return redirect(url_for('index'))

@app.route('/logout')
def logout():
    session.pop('username', None)
    return redirect(url_for('index'))

Remember that Flask sessions are stored on the client side by default (in cookies). For sensitive data, consider using server-side sessions or encrypting the data before storing it in the session.

Common Form Validators Description
DataRequired Field cannot be empty
Email Valid email format
Length String length constraints
EqualTo Field must match another field
Regexp Match regular expression pattern
URL Valid URL format
  • Always validate form data
  • Use CSRF protection for forms
  • Sanitize user input before processing
  • Validate file uploads for type and size
  • Use secure_filename for uploaded files

Error handling is crucial for providing good user experience. Flask allows you to define custom error handlers for different HTTP status codes. This lets you show user-friendly error pages instead of the default browser error messages.

Deployment and Configuration

Deploying a Flask application requires careful consideration of several factors. While Flask's built-in server is convenient for development, it's not suitable for production use. For production deployment, you need a proper WSGI server.

Configuration management is an important aspect of Flask applications. Flask allows you to load configuration from Python files, environment variables, or both. This lets you have different configurations for development, testing, and production environments.

app = Flask(__name__)
app.config.from_object('config.DevelopmentConfig')

Or using environment variables:

app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY') or 'dev-key'

For production deployment, common choices include Gunicorn, uWSGI, or mod_wsgi with Apache. Here's how you might run your application with Gunicorn:

gunicorn -w 4 -b 0.0.0.0:8000 myapp:app

Environment variables are particularly useful for storing sensitive information like database credentials and API keys. Never hardcode sensitive information in your source code. Instead, use environment variables or configuration files that are excluded from version control.

import os

class Config:
    SECRET_KEY = os.environ.get('SECRET_KEY') or 'hard-to-guess-string'
    SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \
        'sqlite:///' + os.path.join(basedir, 'app.db')

class DevelopmentConfig(Config):
    DEBUG = True

class ProductionConfig(Config):
    DEBUG = False

Static file serving in production should be handled by your web server or a CDN rather than Flask itself. This is more efficient and takes load off your application server. For example, with Nginx, you can configure it to serve static files directly:

location /static {
    alias /path/to/your/static/files;
    expires 30d;
}

Database migrations are important when your database schema changes over time. Flask-Migrate is an extension that provides SQLAlchemy database migrations using Alembic. This allows you to make changes to your database schema in a controlled and versioned manner.

from flask_migrate import Migrate

migrate = Migrate(app, db)

Then you can use Flask-Migrate commands:

flask db init
flask db migrate -m "Initial migration"
flask db upgrade

Logging is essential for monitoring your application in production. Flask uses the standard Python logging module. You can configure logging to output to files, syslog, or external services.

import logging
from logging.handlers import RotatingFileHandler

if not app.debug:
    file_handler = RotatingFileHandler('app.log', maxBytes=10240, backupCount=10)
    file_handler.setFormatter(logging.Formatter(
        '%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]'
    ))
    file_handler.setLevel(logging.INFO)
    app.logger.addHandler(file_handler)
    app.logger.setLevel(logging.INFO)
    app.logger.info('Application startup')

Security considerations are paramount when deploying to production. Some key security practices include: - Using HTTPS everywhere - Setting secure cookies - Implementing proper CORS policies - Regularly updating dependencies - Using environment variables for secrets - Implementing rate limiting - Validating all user input

Monitoring and performance should be considered from the start. Tools like Flask-DebugToolbar can help during development, while production monitoring might involve services like Sentry for error tracking and performance monitoring.

Remember that testing your application is crucial before deployment. Flask provides a test client that makes it easy to write tests for your application:

import unittest
from flask import current_app
from app import create_app, db

class BasicsTestCase(unittest.TestCase):
    def setUp(self):
        self.app = create_app('testing')
        self.app_context = self.app.app_context()
        self.app_context.push()
        db.create_all()

    def tearDown(self):
        db.session.remove()
        db.drop_all()
        self.app_context.pop()

    def test_app_exists(self):
        self.assertFalse(current_app is None)

Finally, consider using containerization with Docker for consistent deployment across environments. This ensures that your application runs the same way in development, testing, and production.

Deployment Option Best For Considerations
Gunicorn + Nginx Most production setups Good performance, requires configuration
uWSGI + Nginx High performance needs More complex setup, better for heavy loads
Apache + mod_wsgi Existing Apache infrastructure Integrated with Apache ecosystem
Docker containers Consistent environments Additional complexity, good for microservices
  • Use environment variables for configuration
  • Never run debug mode in production
  • Implement proper error handling
  • Set up monitoring and logging
  • Use HTTPS for all communications

Load testing your application before deployment can help identify performance bottlenecks. Tools like Locust or Apache JMeter can simulate multiple users accessing your application simultaneously.

Advanced Flask Features

As you become more comfortable with Flask, you'll want to explore its more advanced features. These capabilities allow you to build more sophisticated applications while maintaining Flask's simplicity and flexibility.

Blueprints are one of Flask's most powerful features for organizing larger applications. They allow you to divide your application into reusable components. This is particularly useful when building large applications or when you want to create reusable application components.

from flask import Blueprint

auth = Blueprint('auth', __name__)

@auth.route('/login')
def login():
    return 'Login page'

@auth.route('/logout')
def logout():
    return 'Logout page'

Then register the blueprint in your application:

from auth import auth as auth_blueprint
app.register_blueprint(auth_blueprint, url_prefix='/auth')

Application factories are another pattern for creating more flexible applications. Instead of creating your app instance globally, you create a function that returns an app instance. This is useful for testing and for creating multiple instances of your application.

def create_app(config_filename):
    app = Flask(__name__)
    app.config.from_pyfile(config_filename)

    from yourapplication.views.admin import admin
    from yourapplication.views.frontend import frontend
    app.register_blueprint(admin)
    app.register_blueprint(frontend)

    return app

Custom commands can be added to your Flask application using the Click library, which Flask integrates with. This allows you to create command-line utilities for your application.

import click
from flask import current_app

@app.cli.command()
@click.argument('name')
def hello(name):
    click.echo(f'Hello, {name}!')

Signals provide a way to subscribe to events in your application. Flask comes with several built-in signals, and you can also create your own custom signals.

from flask import template_rendered
from contextlib import contextmanager

@contextmanager
def captured_templates(app):
    recorded = []
    def record(sender, template, context, **extra):
        recorded.append((template, context))
    template_rendered.connect(record, app)
    try:
        yield recorded
    finally:
        template_rendered.disconnect(record, app)

WebSockets can be added to Flask applications using extensions like Flask-SocketIO. This allows real-time, bidirectional communication between clients and servers.

from flask_socketio import SocketIO, emit

socketio = SocketIO(app)

@socketio.on('my event')
def handle_my_custom_event(json):
    emit('my response', json, broadcast=True)

Caching can significantly improve performance for applications with expensive operations. Flask-Caching provides a simple interface to add caching to your application.

from flask_caching import Cache

cache = Cache(app, config={'CACHE_TYPE': 'simple'})

@app.route('/expensive')
@cache.cached(timeout=50)
def expensive_operation():
    # Expensive operation here
    return result

RESTful APIs are easy to build with Flask. While you can build APIs with regular routes, extensions like Flask-RESTful provide additional structure and features for building REST APIs.

from flask_restful import Resource, Api

api = Api(app)

class HelloWorld(Resource):
    def get(self):
        return {'hello': 'world'}

api.add_resource(HelloWorld, '/')

Custom error pages make your application more user-friendly. You can create custom error handlers for different HTTP status codes.

@app.errorhandler(404)
def not_found_error(error):
    return render_template('404.html'), 404

@app.errorhandler(500)
def internal_error(error):
    db.session.rollback()
    return render_template('500.html'), 500

Internationalization and localization can be added using Flask-Babel. This allows you to create applications that support multiple languages.

from flask_babel import Babel, gettext

babel = Babel(app)

@app.route('/')
def index():
    return gettext('Hello, World!')

Background tasks can be handled using Celery with Flask. This is useful for long-running operations that you don't want to block your web requests.

from celery import Celery

def make_celery(app):
    celery = Celery(app.import_name)
    celery.conf.update(app.config)
    return celery

celery = make_celery(app)

@celery.task()
def add_together(a, b):
    return a + b
Advanced Feature Use Case Extension Needed
Blueprints Large application organization Built-in
Application factories Testing and multiple instances Built-in
Custom commands CLI utilities Built-in (Click)
WebSockets Real-time communication Flask-SocketIO
Caching Performance optimization Flask-Caching
RESTful APIs API development Flask-RESTful
Background tasks Async processing Celery
  • Use blueprints for large applications
  • Implement application factories for flexibility
  • Create custom commands for maintenance tasks
  • Consider caching for performance optimization
  • Use signals for event-driven programming

Middleware can be used to add functionality to your application's request/response cycle. While Flask doesn't use middleware in the same way as some other frameworks, you can still use WSGI middleware with your application.

from werkzeug.middleware.proxy_fix import ProxyFix

app.wsgi_app = ProxyFix(app.wsgi_app)

Database connection pooling is important for production applications. Most database extensions handle this automatically, but it's good to understand how it works and how to configure it for your specific needs.

Performance optimization becomes more important as your application grows. Techniques include: - Database query optimization - Implementing caching strategies - Using CDNs for static assets - Optimizing template rendering - Implementing pagination for large datasets

Remember that while these advanced features are powerful, you should only use them when they provide real value to your application. Flask's philosophy is to keep things simple, so avoid adding complexity unless it's necessary.

Testing strategies should evolve as your application grows. Consider implementing: - Unit tests for individual components - Integration tests for feature combinations - End-to-end tests for critical user flows - Load tests for performance validation - Security tests for vulnerability assessment

Finally, always keep maintenance in mind. Regular updates to dependencies, monitoring application performance, and periodically reviewing your codebase are all important practices for maintaining a healthy Flask application over time.