Python Web Development with Flask: Building Modern Web Applications
Python Web Development with Flask: Building Modern Web Applications
Flask is a lightweight and flexible Python web framework that's perfect for building web applications and APIs. This guide will walk you through Flask fundamentals and help you build a complete web application with authentication and database integration.
Getting Started with Flask
First, let's set up our development environment:
# Create virtual environment
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
# Install required packages
pip install flask flask-sqlalchemy flask-login flask-migrate python-dotenv
Basic Flask Application
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello, World!'
if __name__ == '__main__':
app.run(debug=True)
Project: Task Management API
Let's build a complete task management application with Flask. This project will include:
- RESTful API endpoints
- User authentication
- Database integration
- Error handling
- API documentation
Project Structure
task_manager/
├── .env
├── config.py
├── requirements.txt
├── run.py
└── app/
├── __init__.py
├── models.py
├── routes.py
├── auth.py
├── utils.py
└── templates/
├── base.html
├── login.html
└── dashboard.html
Configuration (config.py)
import os
from dotenv import load_dotenv
load_dotenv()
class Config:
SECRET_KEY = os.environ.get('SECRET_KEY') or 'your-secret-key'
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \
'sqlite:///app.db'
SQLALCHEMY_TRACK_MODIFICATIONS = False
Application Factory (app/__init__.py)
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
from config import Config
db = SQLAlchemy()
login_manager = LoginManager()
def create_app(config_class=Config):
app = Flask(__name__)
app.config.from_object(config_class)
# Initialize extensions
db.init_app(app)
login_manager.init_app(app)
login_manager.login_view = 'auth.login'
# Register blueprints
from app.routes import main
from app.auth import auth
app.register_blueprint(main)
app.register_blueprint(auth)
return app
Database Models (app/models.py)
from datetime import datetime
from werkzeug.security import generate_password_hash, check_password_hash
from flask_login import UserMixin
from app import db, login_manager
@login_manager.user_loader
def load_user(id):
return User.query.get(int(id))
class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
password_hash = db.Column(db.String(128))
tasks = db.relationship('Task', backref='author', lazy='dynamic')
def set_password(self, password):
self.password_hash = generate_password_hash(password)
def check_password(self, password):
return check_password_hash(self.password_hash, password)
class Task(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(100), nullable=False)
description = db.Column(db.Text)
created_at = db.Column(db.DateTime, default=datetime.utcnow)
due_date = db.Column(db.DateTime)
completed = db.Column(db.Boolean, default=False)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
def to_dict(self):
return {
'id': self.id,
'title': self.title,
'description': self.description,
'created_at': self.created_at.isoformat(),
'due_date': self.due_date.isoformat() if self.due_date else None,
'completed': self.completed
}
Authentication Routes (app/auth.py)
from flask import Blueprint, request, jsonify
from flask_login import login_user, logout_user, login_required
from app.models import User, db
auth = Blueprint('auth', __name__)
@auth.route('/register', methods=['POST'])
def register():
data = request.get_json()
if User.query.filter_by(username=data['username']).first():
return jsonify({'error': 'Username already exists'}), 400
if User.query.filter_by(email=data['email']).first():
return jsonify({'error': 'Email already registered'}), 400
user = User(username=data['username'], email=data['email'])
user.set_password(data['password'])
db.session.add(user)
db.session.commit()
return jsonify({'message': 'User registered successfully'}), 201
@auth.route('/login', methods=['POST'])
def login():
data = request.get_json()
user = User.query.filter_by(username=data['username']).first()
if user and user.check_password(data['password']):
login_user(user)
return jsonify({'message': 'Logged in successfully'})
return jsonify({'error': 'Invalid username or password'}), 401
@auth.route('/logout')
@login_required
def logout():
logout_user()
return jsonify({'message': 'Logged out successfully'})
Task Routes (app/routes.py)
from flask import Blueprint, request, jsonify
from flask_login import login_required, current_user
from app.models import Task, db
from datetime import datetime
main = Blueprint('main', __name__)
@main.route('/tasks', methods=['GET'])
@login_required
def get_tasks():
tasks = current_user.tasks.all()
return jsonify([task.to_dict() for task in tasks])
@main.route('/tasks', methods=['POST'])
@login_required
def create_task():
data = request.get_json()
task = Task(
title=data['title'],
description=data.get('description'),
due_date=datetime.fromisoformat(data['due_date']) if data.get('due_date') else None,
author=current_user
)
db.session.add(task)
db.session.commit()
return jsonify(task.to_dict()), 201
@main.route('/tasks/<int:task_id>', methods=['PUT'])
@login_required
def update_task(task_id):
task = Task.query.get_or_404(task_id)
if task.user_id != current_user.id:
return jsonify({'error': 'Unauthorized'}), 403
data = request.get_json()
task.title = data.get('title', task.title)
task.description = data.get('description', task.description)
task.completed = data.get('completed', task.completed)
if data.get('due_date'):
task.due_date = datetime.fromisoformat(data['due_date'])
db.session.commit()
return jsonify(task.to_dict())
@main.route('/tasks/<int:task_id>', methods=['DELETE'])
@login_required
def delete_task(task_id):
task = Task.query.get_or_404(task_id)
if task.user_id != current_user.id:
return jsonify({'error': 'Unauthorized'}), 403
db.session.delete(task)
db.session.commit()
return '', 204
Error Handling (app/utils.py)
from flask import jsonify
from app import create_app
app = create_app()
@app.errorhandler(404)
def not_found_error(error):
return jsonify({'error': 'Not found'}), 404
@app.errorhandler(500)
def internal_error(error):
db.session.rollback()
return jsonify({'error': 'Internal server error'}), 500
Running the Application (run.py)
from app import create_app, db
app = create_app()
if __name__ == '__main__':
with app.app_context():
db.create_all()
app.run(debug=True)
Testing the API
Here are some example API requests using curl:
# Register a new user
curl -X POST http://localhost:5000/register \
-H "Content-Type: application/json" \
-d '{"username":"testuser","email":"test@example.com","password":"password123"}'
# Login
curl -X POST http://localhost:5000/login \
-H "Content-Type: application/json" \
-d '{"username":"testuser","password":"password123"}'
# Create a task
curl -X POST http://localhost:5000/tasks \
-H "Content-Type: application/json" \
-d '{"title":"Test Task","description":"This is a test task","due_date":"2024-03-01T00:00:00"}'
# Get all tasks
curl http://localhost:5000/tasks
# Update a task
curl -X PUT http://localhost:5000/tasks/1 \
-H "Content-Type: application/json" \
-d '{"completed":true}'
# Delete a task
curl -X DELETE http://localhost:5000/tasks/1
Best Practices
-
Security
- Use HTTPS in production
- Implement rate limiting
- Sanitize user input
- Use secure session management
-
Performance
- Use database indexing
- Implement caching
- Optimize database queries
- Use async operations when appropriate
-
Code Organization
- Follow the Blueprint pattern
- Separate concerns
- Use configuration files
- Implement proper error handling
-
Testing
- Write unit tests
- Use integration tests
- Test error cases
- Use test fixtures
Deployment Considerations
-
Environment Variables
# .env SECRET_KEY=your-secret-key DATABASE_URL=postgresql://user:password@localhost/dbname FLASK_ENV=production
-
Production Server
pip install gunicorn gunicorn "app:create_app()"
-
Database Migrations
from flask_migrate import Migrate migrate = Migrate(app, db) # Create migration flask db init flask db migrate -m "Initial migration" flask db upgrade
Conclusion
Flask provides a flexible and powerful platform for building web applications. This project demonstrates:
- RESTful API development
- User authentication
- Database integration
- Error handling
- Best practices for Flask applications
Continue exploring Flask's ecosystem and build upon this foundation to create more complex applications.