
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.