An Interactive Guide to Python's Core Concepts with Web Development Applications
Analogy: If web development were a construction site, Python would be both the foundation and a versatile multi-tool in your belt. It supports everything from backend logic to data processing.
Python has become one of the most powerful languages for modern web development because of its simplicity, readability, and extensive ecosystem. As a full-stack developer, Python gives you the ability to build complete web applications from the server-side processing to data visualization.
Before diving into code, let's set up a proper development environment optimized for web development:
python -m pip install --upgrade pippip install virtualenv
python -m venv my_project_env
# On Windows:
my_project_env\Scripts\activate
# On macOS/Linux:
source my_project_env/bin/activate
For effective web development with Python, install these tools:
# Web development essentials
pip install django flask requests
# Data processing for web applications
pip install pandas numpy matplotlib
# API and testing tools
pip install httpx pytest selenium
Choose a development environment that supports Python web development:
Analogy: Python syntax is like well-structured English. If programming languages were vehicles, Python would be a bicycle—straightforward to learn, efficient for short trips, and endlessly customizable for different journeys.
# Comments start with a hashtag
# Variables - no type declarations needed
name = "Alice"
age = 29
is_developer = True
# String formatting (f-strings)
greeting = f"Hello, {name}! You are {age} years old."
print(greeting) # Output: Hello, Alice! You are 29 years old.
# Indentation defines blocks (not braces or keywords)
if age > 18:
print("You are an adult")
if is_developer:
print("And a developer!")
else:
print("You are a minor")
Unlike JavaScript or Java, Python uses indentation to define code blocks. This enforces readability and clean code structure:
Following the "Pythonic" way will make your code more maintainable and consistent:
Python offers a rich set of data types particularly useful for web development tasks:
# Numbers
page_count = 42 # integer
response_time = 0.15 # float
# Strings
username = "developer123"
html_content = """
<div class="container">
<h1>Welcome!</h1>
</div>
"""
# Booleans
is_authenticated = True
has_permission = False
# None (similar to null in JavaScript)
user_profile = None
# Lists - ordered, mutable collections (similar to JS arrays)
nav_items = ["Home", "About", "Contact", "Login"]
nav_items.append("Signup") # Adding items
first_item = nav_items[0] # Accessing by index
# Tuples - ordered, immutable collections
point = (10, 20) # Can't be modified after creation
x, y = point # Unpacking values
# Dictionaries - key-value pairs (similar to JS objects)
user = {
"username": "alice_dev",
"email": "alice@example.com",
"role": "admin",
"active": True
}
print(user["email"]) # Accessing by key
# Sets - unordered collections of unique items
permissions = {"read", "write", "delete"}
permissions.add("admin") # Adding items
permissions.add("read") # Adding duplicate (ignored)
"write" in permissions # Membership testing (True)
These data structures map perfectly to web development concepts:
Analogy: Control flow in Python is like the traffic signals in a city. Conditionals are intersections with traffic lights, while loops are roundabouts that let you circle until the right exit appears.
# Basic if-elif-else structure
user_role = "editor"
if user_role == "admin":
print("Full access granted")
elif user_role == "editor":
print("Edit access granted")
elif user_role == "viewer":
print("View access granted")
else:
print("No access")
# Ternary operator (inline conditional)
message = "Welcome back" if is_authenticated else "Please log in"
# Truthiness testing (empty collections evaluate to False)
user_cart = []
if user_cart:
print("Items in cart")
else:
print("Cart is empty")
# For loop - process collections
menu_items = ["Home", "Products", "About", "Contact"]
# Generate HTML list items
html_output = "<ul>"
for item in menu_items:
html_output += f"\n <li>{item}</li>"
html_output += "\n</ul>"
print(html_output)
# Enumerate for index access
for i, item in enumerate(menu_items):
print(f"Menu item {i+1}: {item}")
# Dictionary iteration
user_data = {"name": "Bob", "role": "developer", "active": True}
# Three ways to iterate dictionaries
for key in user_data:
print(f"{key}: {user_data[key]}")
for key, value in user_data.items():
print(f"{key}: {value}")
for value in user_data.values():
print(value)
# While loop - useful for conditions
page_number = 1
has_next_page = True
while has_next_page:
results = fetch_page_data(page_number)
process_results(results)
has_next_page = results.has_more
page_number += 1
def generate_menu(items, current_page):
"""Generate HTML for navigation with active page highlighted"""
menu_html = '<nav class="main-navigation">\n'
menu_html += ' <ul>\n'
for item in items:
active_class = ' class="active"' if item.lower() == current_page.lower() else ''
url = f"/{item.lower().replace(' ', '-')}"
menu_html += f' <li{active_class}><a href="{url}">{item}</a></li>\n'
menu_html += ' </ul>\n'
menu_html += '</nav>'
return menu_html
# Example usage
pages = ["Home", "About Us", "Services", "Contact"]
print(generate_menu(pages, "Services"))
Analogy: Functions in Python are like specialized tools in a workshop. Each performs a specific task efficiently, and together they enable you to build complex projects. Modules are like toolboxes that organize related tools.
# Basic function definition
def greet(name, greeting="Hello"):
"""Return a personalized greeting message."""
return f"{greeting}, {name}!"
# Function calls
print(greet("Alice")) # Uses default greeting
print(greet("Bob", "Welcome")) # Custom greeting
# Keyword arguments for clarity
print(greet(greeting="Hey", name="Charlie"))
# Multiple return values (using tuple unpacking)
def get_user_stats(user_id):
# Imagine this fetches data from a database
return "alice_dev", 204, ["admin", "editor"]
username, post_count, roles = get_user_stats(42)
print(f"{username} has {post_count} posts and roles: {', '.join(roles)}")
# Lambda functions for short operations
users = [
{"name": "Alice", "posts": 20},
{"name": "Bob", "posts": 5},
{"name": "Charlie", "posts": 15}
]
# Sort users by post count (highest first)
users.sort(key=lambda user: user["posts"], reverse=True)
top_user = users[0]["name"]
print(f"Most active user: {top_user}")
# Importing a full module
import random
random_number = random.randint(1, 10)
# Importing specific functions
from datetime import datetime, timedelta
current_time = datetime.now()
tomorrow = current_time + timedelta(days=1)
# Importing with aliases
import pandas as pd
import numpy as np
# Creating your own modules
# In utils.py:
def slugify(text):
"""Convert text to URL-friendly slug"""
return text.lower().replace(" ", "-")
# In main.py:
from utils import slugify
page_url = slugify("Contact Us") # Result: "contact-us"
def validate_form(form_data):
"""Validate user registration form data"""
errors = {}
# Check username (required, 3-20 chars, alphanumeric)
if "username" not in form_data or not form_data["username"]:
errors["username"] = "Username is required"
elif not (3 <= len(form_data["username"]) <= 20):
errors["username"] = "Username must be 3-20 characters"
elif not form_data["username"].isalnum():
errors["username"] = "Username must contain only letters and numbers"
# Check email (required, valid format)
if "email" not in form_data or not form_data["email"]:
errors["email"] = "Email is required"
elif "@" not in form_data["email"] or "." not in form_data["email"]:
errors["email"] = "Please enter a valid email address"
# Check password (required, 8+ chars, contains number)
if "password" not in form_data or not form_data["password"]:
errors["password"] = "Password is required"
elif len(form_data["password"]) < 8:
errors["password"] = "Password must be at least 8 characters"
elif not any(char.isdigit() for char in form_data["password"]):
errors["password"] = "Password must contain at least one number"
return {"is_valid": len(errors) == 0, "errors": errors}
# Example usage
user_data = {
"username": "dev123",
"email": "dev@example.com",
"password": "securepass123"
}
validation_result = validate_form(user_data)
if validation_result["is_valid"]:
print("Form is valid, creating user...")
else:
print("Form validation failed:")
for field, error in validation_result["errors"].items():
print(f"- {field}: {error}")
Analogy: If functions are standalone tools, classes are like automated machines that combine multiple tools with their own materials. Each instance of a class is a separate machine configured for specific needs.
class User:
"""A class representing a website user."""
# Class attribute (shared across all instances)
user_count = 0
def __init__(self, username, email):
"""Initialize a new User instance."""
self.username = username
self.email = email
self.is_active = True
self.posts = []
# Increment user count
User.user_count += 1
def __str__(self):
"""String representation of user."""
return f"User: {self.username} ({self.email})"
def add_post(self, content):
"""Add a new post for this user."""
post = {
"id": len(self.posts) + 1,
"content": content,
"timestamp": datetime.now()
}
self.posts.append(post)
return post["id"]
def get_posts(self):
"""Return all posts by this user."""
return self.posts
def deactivate(self):
"""Deactivate the user account."""
self.is_active = False
@classmethod
def display_count(cls):
"""Display total number of users."""
return f"Total users: {cls.user_count}"
# Creating instances
alice = User("alice_dev", "alice@example.com")
bob = User("bob_coder", "bob@example.com")
# Using methods
alice.add_post("Learning Python OOP today!")
alice.add_post("Python classes are powerful!")
# Accessing attributes
print(alice)
print(f"Active status: {alice.is_active}")
print(f"Posts by {alice.username}:")
for post in alice.get_posts():
print(f"- {post['content']}")
# Using class method
print(User.display_count())
class AdminUser(User):
"""Admin user with extended permissions."""
def __init__(self, username, email, admin_level=1):
# Call parent class initializer
super().__init__(username, email)
self.admin_level = admin_level
self.permissions = ["read", "write", "delete"]
if admin_level > 1:
self.permissions.append("user_management")
if admin_level > 2:
self.permissions.append("system_config")
def __str__(self):
# Override parent method
return f"Admin: {self.username} (Level {self.admin_level})"
def delete_user(self, user):
"""Delete a user (admin only)."""
if "user_management" in self.permissions:
user.deactivate()
return f"User {user.username} has been deactivated"
else:
return "Permission denied: requires higher admin level"
# Creating an admin
super_admin = AdminUser("admin_jane", "admin@example.com", admin_level=3)
# Using inherited and specialized methods
super_admin.add_post("Announcement: System upgrade this weekend")
print(super_admin)
print(f"Permissions: {', '.join(super_admin.permissions)}")
print(super_admin.delete_user(bob))
class Content:
"""Base class for all content types."""
def __init__(self, title, author, slug=None):
self.title = title
self.author = author
self.created_at = datetime.now()
self.updated_at = self.created_at
self.slug = slug or self._generate_slug(title)
self.published = False
def _generate_slug(self, text):
"""Generate a URL slug from the title."""
return text.lower().replace(" ", "-")
def publish(self):
"""Publish the content."""
self.published = True
def update(self, **kwargs):
"""Update content attributes."""
for key, value in kwargs.items():
if hasattr(self, key):
setattr(self, key, value)
self.updated_at = datetime.now()
# Update slug if title changed
if "title" in kwargs:
self.slug = self._generate_slug(self.title)
class Article(Content):
"""Article content type."""
def __init__(self, title, author, body, category, slug=None):
super().__init__(title, author, slug)
self.body = body
self.category = category
self.comments = []
def add_comment(self, user, text):
"""Add a comment to the article."""
comment = {
"id": len(self.comments) + 1,
"user": user,
"text": text,
"timestamp": datetime.now()
}
self.comments.append(comment)
return comment["id"]
def get_excerpt(self, length=100):
"""Get a short excerpt of the article."""
if len(self.body) <= length:
return self.body
return self.body[:length] + "..."
class Page(Content):
"""Static page content type."""
def __init__(self, title, author, body, in_navigation=False, slug=None):
super().__init__(title, author, slug)
self.body = body
self.in_navigation = in_navigation
# Usage example
tech_article = Article(
title="Python for Web Development",
author=alice,
body="Python is an excellent choice for web development because...",
category="Technology"
)
tech_article.publish()
tech_article.add_comment(bob, "Great article! Very informative.")
about_page = Page(
title="About Us",
author=super_admin,
body="We are a team of passionate developers...",
in_navigation=True
)
about_page.publish()
# Content management
content_items = [tech_article, about_page]
for item in content_items:
print(f"{type(item).__name__}: {item.title}")
print(f"Published: {item.published}")
print(f"URL: /content/{item.slug}")
print()
Python excels at interacting with APIs, making it perfect for integrating web services into your applications.
import requests
# Basic GET request
response = requests.get("https://api.example.com/users")
if response.status_code == 200:
users = response.json()
for user in users:
print(f"User: {user['name']}")
else:
print(f"Error: {response.status_code}")
# POST request with data
new_user = {
"name": "Emma",
"email": "emma@example.com",
"role": "developer"
}
response = requests.post(
"https://api.example.com/users",
json=new_user,
headers={"Authorization": "Bearer YOUR_API_KEY"}
)
print(f"Status: {response.status_code}")
print(response.json())
from flask import Flask, request, jsonify
app = Flask(__name__)
# In-memory database (for demonstration)
users = [
{"id": 1, "name": "Alice", "role": "admin"},
{"id": 2, "name": "Bob", "role": "user"}
]
@app.route('/api/users', methods=['GET'])
def get_users():
"""Return all users."""
return jsonify(users)
@app.route('/api/users/', methods=['GET'])
def get_user(user_id):
"""Return a specific user by ID."""
user = next((u for u in users if u["id"] == user_id), None)
if user:
return jsonify(user)
return jsonify({"error": "User not found"}), 404
@app.route('/api/users', methods=['POST'])
def create_user():
"""Create a new user."""
if not request.json or 'name' not in request.json:
return jsonify({"error": "Invalid data"}), 400
new_user = {
"id": max(u["id"] for u in users) + 1,
"name": request.json["name"],
"role": request.json.get("role", "user")
}
users.append(new_user)
return jsonify(new_user), 201
if __name__ == '__main__':
app.run(debug=True)
import requests
from flask import Flask, render_template, request
app = Flask(__name__)
def get_weather(city):
"""Get weather data from OpenWeatherMap API."""
api_key = "YOUR_API_KEY"
url = f"https://api.openweathermap.org/data/2.5/weather?q={city}&units=metric&appid={api_key}"
response = requests.get(url)
if response.status_code == 200:
data = response.json()
return {
"city": data["name"],
"country": data["sys"]["country"],
"temp": data["main"]["temp"],
"description": data["weather"][0]["description"],
"icon": data["weather"][0]["icon"],
"humidity": data["main"]["humidity"],
"wind_speed": data["wind"]["speed"]
}
return None
@app.route('/')
def index():
return render_template('index.html')
@app.route('/weather', methods=['GET'])
def weather():
city = request.args.get('city', 'London')
weather_data = get_weather(city)
if weather_data:
return render_template('weather.html', weather=weather_data)
return render_template('error.html', message="City not found")
if __name__ == '__main__':
app.run(debug=True)
Analogy: If your web application is a restaurant, data handling is the kitchen. Python gives you a complete set of chef's tools to prepare, transform, and serve your data in the most delicious ways.
import json
# Reading JSON data
with open('config.json', 'r') as file:
config = json.load(file)
print(f"App name: {config['app_name']}")
print(f"Version: {config['version']}")
# Converting Python objects to JSON
user = {
"name": "Alice",
"roles": ["admin", "editor"],
"settings": {
"theme": "dark",
"notifications": True
}
}
# Convert to JSON string
json_data = json.dumps(user, indent=4)
print(json_data)
# Write to file
with open('user.json', 'w') as file:
json.dump(user, file, indent=4)
import pandas as pd
import matplotlib.pyplot as plt
# Load data from CSV
df = pd.read_csv('website_traffic.csv')
# Data exploration
print(df.head()) # First 5 rows
print(df.describe()) # Statistical summary
# Data transformation
# Add day of week column
df['date'] = pd.to_datetime(df['date'])
df['day_of_week'] = df['date'].dt.day_name()
# Filter data
weekend_traffic = df[df['day_of_week'].isin(['Saturday', 'Sunday'])]
# Aggregation
daily_traffic = df.groupby('day_of_week')['visitors'].mean().reset_index()
# Sorting
daily_traffic = daily_traffic.sort_values('visitors', ascending=False)
# Data visualization
plt.figure(figsize=(10, 6))
plt.bar(daily_traffic['day_of_week'], daily_traffic['visitors'])
plt.title('Average Daily Website Traffic')
plt.xlabel('Day of Week')
plt.ylabel('Average Visitors')
plt.savefig('static/traffic_chart.png')
# Export to CSV for web display
daily_traffic.to_csv('static/daily_traffic.csv', index=False)
import requests
from bs4 import BeautifulSoup
# Fetch website content
url = "https://example.com/blog"
response = requests.get(url)
html_content = response.text
# Parse HTML
soup = BeautifulSoup(html_content, 'html.parser')
# Extract data
articles = []
for article in soup.select('.blog-post'):
title = article.select_one('.post-title').text.strip()
author = article.select_one('.post-author').text.strip()
date = article.select_one('.post-date').text.strip()
articles.append({
"title": title,
"author": author,
"date": date,
"url": article.select_one('a')['href']
})
# Use the extracted data
for article in articles:
print(f"Title: {article['title']}")
print(f"By {article['author']} on {article['date']}")
print(f"URL: {article['url']}")
print()
import pandas as pd
import json
from flask import Flask, render_template, jsonify
app = Flask(__name__)
def load_and_process_data():
"""Load and process analytics data."""
# Load raw data
df = pd.read_csv('analytics_data.csv')
# Process data
df['date'] = pd.to_datetime(df['date'])
df['month'] = df['date'].dt.strftime('%b %Y')
# Monthly trends
monthly = df.groupby('month').agg({
'visitors': 'sum',
'pageviews': 'sum',
'bounce_rate': 'mean'
}).reset_index()
# Source breakdown
source_data = df.groupby('traffic_source').agg({
'visitors': 'sum'
}).reset_index()
source_data['percentage'] = (source_data['visitors'] / source_data['visitors'].sum() * 100).round(1)
# Page performance
page_data = df.groupby('page_path').agg({
'pageviews': 'sum',
'avg_time_on_page': 'mean'
}).sort_values('pageviews', ascending=False).head(10).reset_index()
return {
'monthly_trends': monthly.to_dict(orient='records'),
'traffic_sources': source_data.to_dict(orient='records'),
'top_pages': page_data.to_dict(orient='records')
}
@app.route('/')
def dashboard():
"""Render the analytics dashboard."""
return render_template('dashboard.html')
@app.route('/api/analytics')
def analytics_data():
"""API endpoint for analytics data."""
data = load_and_process_data()
return jsonify(data)
if __name__ == '__main__':
app.run(debug=True)
Python offers several powerful frameworks for web development, each with its own strengths and best use cases.
# models.py
from django.db import models
from django.contrib.auth.models import User
class Product(models.Model):
name = models.CharField(max_length=100)
description = models.TextField()
price = models.DecimalField(max_digits=10, decimal_places=2)
in_stock = models.BooleanField(default=True)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.name
class Review(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name='reviews')
user = models.ForeignKey(User, on_delete=models.CASCADE)
rating = models.IntegerField()
comment = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
unique_together = ['product', 'user']
def __str__(self):
return f"{self.user.username}'s review of {self.product.name}"
# views.py
from django.shortcuts import render, get_object_or_404
from django.http import JsonResponse
from .models import Product, Review
def product_list(request):
products = Product.objects.filter(in_stock=True)
return render(request, 'products/list.html', {'products': products})
def product_detail(request, product_id):
product = get_object_or_404(Product, id=product_id)
return render(request, 'products/detail.html', {'product': product})
def product_api(request, product_id):
product = get_object_or_404(Product, id=product_id)
data = {
'id': product.id,
'name': product.name,
'price': float(product.price),
'in_stock': product.in_stock,
'reviews': [
{
'user': review.user.username,
'rating': review.rating,
'comment': review.comment
}
for review in product.reviews.all()
]
}
return JsonResponse(data)
from flask import Flask, render_template, request, redirect, url_for, jsonify
from flask_sqlalchemy import SQLAlchemy
from werkzeug.security import generate_password_hash, check_password_hash
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///app.db'
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)
password_hash = db.Column(db.String(128))
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)
# Routes
@app.route('/')
def index():
return render_template('index.html')
@app.route('/register', methods=['GET', 'POST'])
def register():
if request.method == 'POST':
username = request.form['username']
email = request.form['email']
password = request.form['password']
# Check if user exists
if User.query.filter_by(username=username).first():
return render_template('register.html', error='Username already exists')
# Create new user
user = User(username=username, email=email)
user.set_password(password)
db.session.add(user)
db.session.commit()
return redirect(url_for('login'))
return render_template('register.html')
@app.route('/api/users')
def api_users():
users = User.query.all()
return jsonify([
{'id': user.id, 'username': user.username, 'email': user.email}
for user in users
])
if __name__ == '__main__':
db.create_all()
app.run(debug=True)
from fastapi import FastAPI, HTTPException, Depends
from pydantic import BaseModel, EmailStr
from typing import List, Optional
import databases
import sqlalchemy
from sqlalchemy import Column, Integer, String, Boolean
# Database setup
DATABASE_URL = "sqlite:///./app.db"
database = databases.Database(DATABASE_URL)
metadata = sqlalchemy.MetaData()
# Models
class UserCreate(BaseModel):
username: str
email: EmailStr
password: str
class User(BaseModel):
id: int
username: str
email: EmailStr
is_active: bool
# Database table
users = sqlalchemy.Table(
"users",
metadata,
Column("id", Integer, primary_key=True),
Column("username", String, unique=True),
Column("email", String, unique=True),
Column("password", String),
Column("is_active", Boolean, default=True),
)
engine = sqlalchemy.create_engine(
DATABASE_URL, connect_args={"check_same_thread": False}
)
metadata.create_all(engine)
# FastAPI app
app = FastAPI(title="User API")
@app.on_event("startup")
async def startup():
await database.connect()
@app.on_event("shutdown")
async def shutdown():
await database.disconnect()
@app.post("/users/", response_model=User)
async def create_user(user: UserCreate):
# Check if username exists
query = users.select().where(users.c.username == user.username)
existing_user = await database.fetch_one(query)
if existing_user:
raise HTTPException(status_code=400, detail="Username already registered")
# Create user
query = users.insert().values(
username=user.username,
email=user.email,
password=user.password, # In production, hash this!
is_active=True
)
user_id = await database.execute(query)
return {**user.dict(), "id": user_id, "is_active": True}
@app.get("/users/", response_model=List[User])
async def read_users(skip: int = 0, limit: int = 100):
query = users.select().offset(skip).limit(limit)
return await database.fetch_all(query)
@app.get("/users/{user_id}", response_model=User)
async def read_user(user_id: int):
query = users.select().where(users.c.id == user_id)
user = await database.fetch_one(query)
if not user:
raise HTTPException(status_code=404, detail="User not found")
return user
As you become more comfortable with Python, these advanced concepts will help you build more sophisticated web applications.
import asyncio
import aiohttp
import time
async def fetch_url(session, url):
"""Fetch a single URL asynchronously."""
start = time.time()
async with session.get(url) as response:
data = await response.text()
elapsed = time.time() - start
print(f"Fetched {url} in {elapsed:.2f} seconds")
return data
async def fetch_all_urls(urls):
"""Fetch multiple URLs concurrently."""
async with aiohttp.ClientSession() as session:
tasks = [fetch_url(session, url) for url in urls]
# Wait for all requests to complete
results = await asyncio.gather(*tasks)
return results
# Example usage
async def main():
urls = [
"https://example.com",
"https://example.org",
"https://example.net"
]
start = time.time()
results = await fetch_all_urls(urls)
elapsed = time.time() - start
print(f"Fetched {len(urls)} URLs in {elapsed:.2f} seconds total")
# Process results
for i, data in enumerate(results):
print(f"URL {i+1} data length: {len(data)} bytes")
# Run the async code
asyncio.run(main())
from functools import wraps
from flask import Flask, request, jsonify
import time
app = Flask(__name__)
# Custom decorator for authentication
def require_api_key(f):
@wraps(f)
def decorated_function(*args, **kwargs):
api_key = request.headers.get('X-API-Key')
if not api_key or api_key != 'YOUR_SECRET_KEY':
return jsonify({"error": "Unauthorized - missing or invalid API key"}), 401
return f(*args, **kwargs)
return decorated_function
# Custom decorator for logging
def log_request(f):
@wraps(f)
def decorated_function(*args, **kwargs):
start_time = time.time()
response = f(*args, **kwargs)
duration = time.time() - start_time
print(f"Request to {request.path} took {duration:.2f} seconds")
return response
return decorated_function
# Custom decorator for rate limiting
def rate_limit(max_requests=100, window=60):
def decorator(f):
# This would typically use Redis or similar to track requests
requests = {}
@wraps(f)
def decorated_function(*args, **kwargs):
client_ip = request.remote_addr
current_time = time.time()
# Clean up old requests
requests[client_ip] = [t for t in requests.get(client_ip, [])
if current_time - t < window]
# Check if rate limit exceeded
if len(requests.get(client_ip, [])) >= max_requests:
return jsonify({"error": "Rate limit exceeded"}), 429
# Add current request
requests.setdefault(client_ip, []).append(current_time)
return f(*args, **kwargs)
return decorated_function
return decorator
# Using the decorators
@app.route('/api/data')
@require_api_key
@log_request
@rate_limit(max_requests=5, window=60)
def get_data():
return jsonify({
"message": "Data access granted",
"data": [1, 2, 3, 4, 5]
})
if __name__ == '__main__':
app.run(debug=True)
class DatabaseConnection:
"""Context manager for database connections."""
def __init__(self, connection_string):
self.connection_string = connection_string
self.connection = None
def __enter__(self):
"""Open database connection."""
print(f"Connecting to database at {self.connection_string}")
# In a real app, this would be something like:
# self.connection = database.connect(self.connection_string)
self.connection = {"connected": True} # Dummy connection for example
return self.connection
def __exit__(self, exc_type, exc_val, exc_tb):
"""Close database connection."""
print("Closing database connection")
if self.connection:
# In a real app: self.connection.close()
self.connection = None
# Return False to propagate exceptions
return False
# Using the context manager
def get_user_data(user_id):
with DatabaseConnection("postgresql://user:pass@localhost/mydb") as db:
# In a real app: result = db.execute("SELECT * FROM users WHERE id = %s", (user_id,))
# For example purposes:
print(f"Fetching data for user {user_id}")
result = {"id": user_id, "name": "Example User"}
return result
# Example usage
user_data = get_user_data(42)
print(user_data)
from sqlalchemy import create_engine, Column, Integer, String, ForeignKey, Boolean, Table
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, relationship, scoped_session
from contextlib import contextmanager
# Database setup
engine = create_engine('sqlite:///advanced_app.db')
Base = declarative_base()
session_factory = sessionmaker(bind=engine)
Session = scoped_session(session_factory)
# Context manager for database sessions
@contextmanager
def db_session():
"""Provide a transactional scope for database operations."""
session = Session()
try:
yield session
session.commit()
except:
session.rollback()
raise
finally:
session.close()
# Association table for many-to-many relationship
user_roles = Table('user_roles', Base.metadata,
Column('user_id', Integer, ForeignKey('users.id')),
Column('role_id', Integer, ForeignKey('roles.id'))
)
# Models
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
username = Column(String, unique=True, nullable=False)
email = Column(String, unique=True, nullable=False)
active = Column(Boolean, default=True)
# Relationships
roles = relationship('Role', secondary=user_roles, back_populates='users')
posts = relationship('Post', back_populates='author')
def __repr__(self):
return f"User(username='{self.username}', email='{self.email}')"
class Role(Base):
__tablename__ = 'roles'
id = Column(Integer, primary_key=True)
name = Column(String, unique=True, nullable=False)
# Relationships
users = relationship('User', secondary=user_roles, back_populates='roles')
def __repr__(self):
return f"Role(name='{self.name}')"
class Post(Base):
__tablename__ = 'posts'
id = Column(Integer, primary_key=True)
title = Column(String, nullable=False)
content = Column(String)
author_id = Column(Integer, ForeignKey('users.id'), nullable=False)
# Relationships
author = relationship('User', back_populates='posts')
def __repr__(self):
return f"Post(title='{self.title}')"
# Create tables
Base.metadata.create_all(engine)
# Example usage
def create_sample_data():
with db_session() as session:
# Create roles
admin_role = Role(name='admin')
user_role = Role(name='user')
session.add_all([admin_role, user_role])
# Create users
alice = User(username='alice', email='alice@example.com')
bob = User(username='bob', email='bob@example.com')
# Assign roles
alice.roles.append(admin_role)
alice.roles.append(user_role)
bob.roles.append(user_role)
session.add_all([alice, bob])
# Create posts
post1 = Post(title='Hello World', content='First post content', author=alice)
post2 = Post(title='Python Tips', content='Python tips content', author=alice)
post3 = Post(title='Web Development', content='Web dev content', author=bob)
session.add_all([post1, post2, post3])
def get_user_with_posts(username):
with db_session() as session:
user = session.query(User).filter_by(username=username).first()
if not user:
return None
# Build user data with posts and roles
data = {
'id': user.id,
'username': user.username,
'email': user.email,
'roles': [role.name for role in user.roles],
'posts': [
{'id': post.id, 'title': post.title, 'content': post.content}
for post in user.posts
]
}
return data
# Run the examples
create_sample_data()
user_data = get_user_with_posts('alice')
print(user_data)
Security is a critical concern for web applications. Here are essential practices to keep your Python web apps secure:
from flask import Flask, request, render_template
import re
import bleach
app = Flask(__name__)
def validate_username(username):
"""Validate username format (alphanumeric, 3-20 chars)."""
if not username:
return False
pattern = re.compile(r'^[a-zA-Z0-9_]{3,20}$')
return bool(pattern.match(username))
def sanitize_html(content):
"""Sanitize HTML content to prevent XSS attacks."""
# Allow specific tags and attributes
allowed_tags = ['p', 'br', 'strong', 'em', 'u', 'a', 'ul', 'ol', 'li']
allowed_attrs = {'a': ['href', 'title']}
# Clean the content
clean_content = bleach.clean(
content,
tags=allowed_tags,
attributes=allowed_attrs,
strip=True
)
return clean_content
@app.route('/submit', methods=['POST'])
def submit_comment():
username = request.form.get('username', '')
comment = request.form.get('comment', '')
errors = []
if not validate_username(username):
errors.append('Invalid username format')
if not comment:
errors.append('Comment cannot be empty')
if errors:
return render_template('form.html', errors=errors)
# Sanitize user input before storing/displaying
clean_comment = sanitize_html(comment)
# Process the comment...
return render_template('success.html')
from flask import Flask, request, session, redirect, url_for
from werkzeug.security import generate_password_hash, check_password_hash
import secrets
import datetime
app = Flask(__name__)
app.config['SECRET_KEY'] = secrets.token_hex(16)
app.config['PERMANENT_SESSION_LIFETIME'] = datetime.timedelta(days=7)
# Dummy user database (use a real database in production)
users_db = {
'admin': {
'password_hash': generate_password_hash('secure_password'),
'role': 'admin'
}
}
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
username = request.form.get('username')
password = request.form.get('password')
user = users_db.get(username)
if not user or not check_password_hash(user['password_hash'], password):
return 'Invalid username or password', 401
# Set session data
session.clear()
session.permanent = True
session['user_id'] = username
session['role'] = user['role']
# Regenerate session ID to prevent session fixation
session.modified = True
return redirect(url_for('dashboard'))
return render_template('login.html')
def login_required(view_func):
@wraps(view_func)
def decorated_view(*args, **kwargs):
if 'user_id' not in session:
return redirect(url_for('login'))
return view_func(*args, **kwargs)
return decorated_view
@app.route('/dashboard')
@login_required
def dashboard():
return f"Welcome, {session['user_id']}!"
@app.route('/logout')
def logout():
session.clear()
return redirect(url_for('login'))
from sqlalchemy import create_engine, text
from sqlalchemy.orm import sessionmaker
# Secure connection with parameterized queries
engine = create_engine('postgresql://user:password@localhost/mydb')
Session = sessionmaker(bind=engine)
def get_user_by_id(user_id):
"""Secure way to query a user by ID."""
with Session() as session:
# Use parameterized queries to prevent SQL injection
query = text("SELECT * FROM users WHERE id = :user_id")
result = session.execute(query, {"user_id": user_id})
return result.fetchone()
def search_users(search_term):
"""Secure way to search users."""
with Session() as session:
# NEVER do this (vulnerable to SQL injection):
# query = f"SELECT * FROM users WHERE username LIKE '%{search_term}%'"
# Instead, use parameterized queries:
query = text("SELECT * FROM users WHERE username LIKE :search")
result = session.execute(query, {"search": f"%{search_term}%"})
return result.fetchall()
from flask import Flask, request, jsonify
from functools import wraps
import jwt
import datetime
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key'
def generate_token(user_id):
"""Generate a JWT token for a user."""
payload = {
'exp': datetime.datetime.utcnow() + datetime.timedelta(days=1),
'iat': datetime.datetime.utcnow(),
'sub': user_id
}
return jwt.encode(
payload,
app.config['SECRET_KEY'],
algorithm='HS256'
)
def token_required(f):
@wraps(f)
def decorated(*args, **kwargs):
token = None
# Get token from Authorization header
if 'Authorization' in request.headers:
auth_header = request.headers['Authorization']
if auth_header.startswith('Bearer '):
token = auth_header.split(' ')[1]
if not token:
return jsonify({'message': 'Token is missing'}), 401
try:
# Decode and validate token
payload = jwt.decode(
token,
app.config['SECRET_KEY'],
algorithms=['HS256']
)
user_id = payload['sub']
except jwt.ExpiredSignatureError:
return jsonify({'message': 'Token has expired'}), 401
except jwt.InvalidTokenError:
return jsonify({'message': 'Invalid token'}), 401
return f(user_id, *args, **kwargs)
return decorated
@app.route('/api/protected', methods=['GET'])
@token_required
def protected_resource(user_id):
return jsonify({'message': f'Protected data for user {user_id}'})
from flask import Flask, request, redirect
app = Flask(__name__)
@app.after_request
def add_security_headers(response):
"""Add security headers to all responses."""
# Prevent content type sniffing
response.headers['X-Content-Type-Options'] = 'nosniff'
# Enable XSS protection in browsers
response.headers['X-XSS-Protection'] = '1; mode=block'
# Prevent clickjacking
response.headers['X-Frame-Options'] = 'DENY'
# Content Security Policy
response.headers['Content-Security-Policy'] = "default-src 'self'"
# HSTS (HTTP Strict Transport Security)
response.headers['Strict-Transport-Security'] = 'max-age=31536000; includeSubDomains'
return response
@app.before_request
def enforce_https():
"""Redirect HTTP requests to HTTPS."""
if request.headers.get('X-Forwarded-Proto') == 'http':
url = request.url.replace('http://', 'https://', 1)
return redirect(url, code=301)
Making your Python web applications faster and more efficient:
import cProfile
import pstats
import time
def profile_function(func, *args, **kwargs):
"""Profile a function's performance."""
profiler = cProfile.Profile()
profiler.enable()
result = func(*args, **kwargs)
profiler.disable()
stats = pstats.Stats(profiler).sort_stats('cumtime')
stats.print_stats(10) # Print top 10 time-consuming functions
return result
# Function benchmark decorator
def benchmark(func):
"""Decorator to measure function execution time."""
@wraps(func)
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"{func.__name__} executed in {end_time - start_time:.6f} seconds")
return result
return wrapper
# Usage example
@benchmark
def expensive_operation(n):
"""A sample computationally expensive operation."""
result = 0
for i in range(n):
result += i * i
return result
# Profile the function
profile_function(expensive_operation, 1000000)
from flask import Flask, request
from functools import lru_cache
import time
import redis
app = Flask(__name__)
cache = redis.Redis(host='localhost', port=6379)
# In-memory caching with LRU strategy
@lru_cache(maxsize=100)
def fibonacci(n):
"""Calculate the nth Fibonacci number with caching."""
if n <= 1:
return n
return fibonacci(n-1) + fibonacci(n-2)
# Redis-based function result caching
def cache_result(ttl=60):
"""Cache function results in Redis."""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
# Create a cache key based on function name and arguments
cache_key = f"{func.__name__}:{str(args)}:{str(kwargs)}"
# Try to get from cache
cached_result = cache.get(cache_key)
if cached_result:
return eval(cached_result.decode('utf-8'))
# Calculate result and store in cache
result = func(*args, **kwargs)
cache.setex(cache_key, ttl, str(result))
return result
return wrapper
return decorator
# Flask response caching
@app.route('/api/data')
@cache_result(ttl=300) # Cache for 5 minutes
def get_expensive_data():
"""Return data from an expensive operation."""
# Simulate expensive operation
time.sleep(2)
return {"data": [1, 2, 3, 4, 5], "timestamp": time.time()}
from sqlalchemy import create_engine, Column, Integer, String, ForeignKey, Index, text
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, relationship, joinedload
engine = create_engine('postgresql://user:password@localhost/mydb')
Base = declarative_base()
Session = sessionmaker(bind=engine)
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
username = Column(String, unique=True, nullable=False)
email = Column(String, nullable=False)
# Define indexes for better query performance
__table_args__ = (
Index('idx_username', 'username'),
Index('idx_email', 'email'),
)
posts = relationship('Post', back_populates='author')
class Post(Base):
__tablename__ = 'posts'
id = Column(Integer, primary_key=True)
title = Column(String, nullable=False)
content = Column(String)
author_id = Column(Integer, ForeignKey('users.id'), nullable=False)
# Define indexes
__table_args__ = (
Index('idx_author_id', 'author_id'),
)
author = relationship('User', back_populates='posts')
# Efficient queries with eager loading
def get_user_with_posts(user_id):
"""Get a user and their posts efficiently."""
with Session() as session:
# Use eager loading to avoid N+1 query problem
user = session.query(User).options(
joinedload(User.posts)
).filter_by(id=user_id).first()
return user
# Batch processing for large datasets
def process_users_in_batches(batch_size=100):
"""Process users in batches to conserve memory."""
with Session() as session:
# Get total count
total = session.query(User).count()
# Process in batches
for offset in range(0, total, batch_size):
batch = session.query(User).offset(offset).limit(batch_size).all()
for user in batch:
# Process each user
print(f"Processing user: {user.username}")
# Clear session to free memory
session.expunge_all()
# Raw SQL for performance-critical operations
def get_user_stats():
"""Get user statistics using optimized SQL."""
with engine.connect() as conn:
result = conn.execute(text("""
SELECT
COUNT(*) as total_users,
AVG(post_count) as avg_posts_per_user
FROM (
SELECT
users.id,
COUNT(posts.id) as post_count
FROM users
LEFT JOIN posts ON users.id = posts.author_id
GROUP BY users.id
) as user_posts
"""))
return result.fetchone()
from flask import Flask, jsonify, request
import asyncio
import aiohttp
import threading
import queue
import time
app = Flask(__name__)
# Task queue for background processing
task_queue = queue.Queue()
def worker():
"""Background worker to process tasks."""
while True:
# Get task from queue
task = task_queue.get()
if task is None:
break
# Process task
func, args, kwargs = task
try:
func(*args, **kwargs)
except Exception as e:
print(f"Error processing task: {e}")
# Mark task as done
task_queue.task_done()
# Start worker thread
worker_thread = threading.Thread(target=worker, daemon=True)
worker_thread.start()
def process_data_async(data):
"""Process data asynchronously."""
# Simulate long-running task
time.sleep(5)
# Do something with the data
print(f"Processed data: {data}")
@app.route('/submit', methods=['POST'])
def submit_data():
"""Submit data for asynchronous processing."""
data = request.json.get('data')
# Queue task for background processing
task_queue.put((process_data_async, (data,), {}))
return jsonify({'status': 'queued'})
# Async route with aiohttp
@app.route('/fetch-multiple', methods=['GET'])
def fetch_multiple():
"""Start multiple API requests asynchronously."""
urls = request.args.getlist('url')
# Use asyncio for concurrent HTTP requests
async def fetch_all():
async with aiohttp.ClientSession() as session:
tasks = [fetch_url(session, url) for url in urls]
results = await asyncio.gather(*tasks)
return results
async def fetch_url(session, url):
async with session.get(url) as response:
return await response.json()
# Run async code in a separate thread
loop = asyncio.new_event_loop()
results = loop.run_until_complete(fetch_all())
loop.close()
return jsonify(results)
Integrating Python backends with modern frontend technologies:
from flask import Flask, jsonify, request
from flask_cors import CORS
import json
app = Flask(__name__)
CORS(app) # Enable Cross-Origin Resource Sharing
# Product data store
products = [
{"id": 1, "name": "Laptop", "price": 999.99, "category": "Electronics"},
{"id": 2, "name": "Headphones", "price": 149.99, "category": "Electronics"},
{"id": 3, "name": "Coffee Maker", "price": 79.99, "category": "Kitchen"}
]
@app.route('/api/products', methods=['GET'])
def get_products():
"""Get all products with optional filtering."""
category = request.args.get('category')
min_price = request.args.get('min_price')
max_price = request.args.get('max_price')
filtered_products = products
if category:
filtered_products = [p for p in filtered_products if p['category'] == category]
if min_price:
filtered_products = [p for p in filtered_products if p['price'] >= float(min_price)]
if max_price:
filtered_products = [p for p in filtered_products if p['price'] <= float(max_price)]
return jsonify(filtered_products)
@app.route('/api/products/', methods=['GET'])
def get_product(product_id):
"""Get a specific product by ID."""
product = next((p for p in products if p['id'] == product_id), None)
if product:
return jsonify(product)
return jsonify({"error": "Product not found"}), 404
@app.route('/api/products', methods=['POST'])
def add_product():
"""Add a new product."""
if not request.json:
return jsonify({"error": "Invalid product data"}), 400
new_product = {
"id": max(p['id'] for p in products) + 1,
"name": request.json.get('name'),
"price": request.json.get('price'),
"category": request.json.get('category')
}
products.append(new_product)
return jsonify(new_product), 201
if __name__ == '__main__':
app.run(debug=True)
from flask import Flask, render_template, request, redirect, url_for
import datetime
app = Flask(__name__)
# Sample data
tasks = [
{"id": 1, "title": "Complete project proposal", "completed": False, "due_date": "2025-06-15"},
{"id": 2, "title": "Review code changes", "completed": True, "due_date": "2025-06-10"}
]
@app.route('/')
def index():
"""Render the main page with tasks."""
# Get filter parameter
filter_type = request.args.get('filter', 'all')
# Apply filter
filtered_tasks = tasks
if filter_type == 'active':
filtered_tasks = [task for task in tasks if not task['completed']]
elif filter_type == 'completed':
filtered_tasks = [task for task in tasks if task['completed']]
# Parse due dates for display
for task in filtered_tasks:
due_date = datetime.datetime.strptime(task['due_date'], "%Y-%m-%d").date()
task['formatted_date'] = due_date.strftime("%b %d, %Y")
task['overdue'] = due_date < datetime.date.today() and not task['completed']
return render_template(
'tasks.html',
tasks=filtered_tasks,
filter=filter_type,
now=datetime.datetime.now().strftime("%Y-%m-%d")
)
@app.route('/add', methods=['POST'])
def add_task():
"""Add a new task."""
title = request.form.get('title', '').strip()
due_date = request.form.get('due_date')
if title and due_date:
new_task = {
"id": max(task['id'] for task in tasks) + 1 if tasks else 1,
"title": title,
"completed": False,
"due_date": due_date
}
tasks.append(new_task)
return redirect(url_for('index'))
@app.route('/toggle/')
def toggle_task(task_id):
"""Toggle task completion status."""
task = next((t for t in tasks if t['id'] == task_id), None)
if task:
task['completed'] = not task['completed']
return redirect(url_for('index'))
from flask import Flask
from flask_graphql import GraphQLView
import graphene
from graphene import ObjectType, String, Int, List, Field, Boolean
app = Flask(__name__)
# Sample data
authors = [
{"id": "1", "name": "J.K. Rowling", "country": "UK"},
{"id": "2", "name": "George R.R. Martin", "country": "USA"}
]
books = [
{"id": "1", "title": "Harry Potter", "pages": 320, "author_id": "1"},
{"id": "2", "title": "Game of Thrones", "pages": 604, "author_id": "2"},
{"id": "3", "title": "Fantastic Beasts", "pages": 234, "author_id": "1"}
]
# GraphQL Schema
class Author(ObjectType):
id = String()
name = String()
country = String()
books = List(lambda: Book)
def resolve_books(parent, info):
return [book for book in books if book["author_id"] == parent["id"]]
class Book(ObjectType):
id = String()
title = String()
pages = Int()
author_id = String()
author = Field(Author)
def resolve_author(parent, info):
return next((author for author in authors if author["id"] == parent["author_id"]), None)
class Query(ObjectType):
authors = List(Author)
books = List(Book)
author = Field(Author, id=String())
book = Field(Book, id=String())
def resolve_authors(parent, info):
return authors
def resolve_books(parent, info):
return books
def resolve_author(parent, info, id):
return next((author for author in authors if author["id"] == id), None)
def resolve_book(parent, info, id):
return next((book for book in books if book["id"] == id), None)
# Create schema
schema = graphene.Schema(query=Query)
# Add GraphQL view
app.add_url_rule(
'/graphql',
view_func=GraphQLView.as_view(
'graphql',
schema=schema,
graphiql=True # Enable GraphiQL interface for testing
)
)
if __name__ == '__main__':
app.run(debug=True)
from flask import Flask, render_template
from flask_socketio import SocketIO, emit, join_room, leave_room
import datetime
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key'
socketio = SocketIO(app, cors_allowed_origins="*")
# Store connected users and chat rooms
users = {}
chat_rooms = {
'general': {'users': set(), 'messages': []},
'support': {'users': set(), 'messages': []}
}
@app.route('/')
def index():
"""Render chat application."""
return render_template('chat.html', rooms=list(chat_rooms.keys()))
@socketio.on('connect')
def handle_connect():
"""Handle client connection."""
print(f"Client connected: {request.sid}")
@socketio.on('disconnect')
def handle_disconnect():
"""Handle client disconnection."""
user_id = None
for uid, sid in users.items():
if sid == request.sid:
user_id = uid
break
if user_id:
# Remove user from rooms
for room in chat_rooms:
if user_id in chat_rooms[room]['users']:
chat_rooms[room]['users'].remove(user_id)
leave_room(room)
# Notify other users
emit('user_left', {
'user_id': user_id,
'time': datetime.datetime.now().strftime('%H:%M:%S')
}, room=room)
# Remove user
del users[user_id]
@socketio.on('login')
def handle_login(data):
"""Handle user login."""
user_id = data['user_id']
users[user_id] = request.sid
# Auto-join general room
join_room('general')
chat_rooms['general']['users'].add(user_id)
# Send existing messages
emit('history', chat_rooms['general']['messages'])
# Notify other users
emit('user_joined', {
'user_id': user_id,
'time': datetime.datetime.now().strftime('%H:%M:%S')
}, room='general')
@socketio.on('join_room')
def handle_join_room(data):
"""Handle room joining."""
room = data['room']
user_id = data['user_id']
if room in chat_rooms:
join_room(room)
chat_rooms[room]['users'].add(user_id)
# Send existing messages
emit('history', chat_rooms[room]['messages'])
# Notify other users
emit('user_joined', {
'user_id': user_id,
'time': datetime.datetime.now().strftime('%H:%M:%S')
}, room=room)
@socketio.on('leave_room')
def handle_leave_room(data):
"""Handle room leaving."""
room = data['room']
user_id = data['user_id']
if room in chat_rooms and user_id in chat_rooms[room]['users']:
chat_rooms[room]['users'].remove(user_id)
leave_room(room)
# Notify other users
emit('user_left', {
'user_id': user_id,
'time': datetime.datetime.now().strftime('%H:%M:%S')
}, room=room)
@socketio.on('message')
def handle_message(data):
"""Handle chat message."""
room = data['room']
user_id = data['user_id']
message = data['message']
if room in chat_rooms:
# Create message object
msg = {
'user_id': user_id,
'message': message,
'time': datetime.datetime.now().strftime('%H:%M:%S')
}
# Store message
chat_rooms[room]['messages'].append(msg)
# Send to all in room
emit('new_message', msg, room=room)
if __name__ == '__main__':
socketio.run(app, debug=True)
Moving your Python application from development to production:
# wsgi.py
from myapp import app as application
if __name__ == '__main__':
application.run()
# Gunicorn configuration (gunicorn_config.py)
bind = "0.0.0.0:8000"
workers = 4 # (2 * CPU cores) + 1
worker_class = "sync" # Options: sync, eventlet, gevent
timeout = 60
keepalive = 5
# Supervisor configuration (supervisor.conf)
[program:myapp]
command=/path/to/venv/bin/gunicorn -c gunicorn_config.py wsgi:application
directory=/path/to/project
user=www-data
autostart=true
autorestart=true
redirect_stderr=true
stdout_logfile=/var/log/myapp/gunicorn.log
# Dockerfile
FROM python:3.10-slim
WORKDIR /app
# Install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy application code
COPY . .
# Set environment variables
ENV FLASK_APP=app.py
ENV FLASK_ENV=production
# Expose port
EXPOSE 5000
# Run the application with Gunicorn
CMD ["gunicorn", "--bind", "0.0.0.0:5000", "wsgi:application"]
# docker-compose.yml
version: '3'
services:
web:
build: .
ports:
- "5000:5000"
environment:
- DATABASE_URL=postgresql://user:password@db:5432/mydb
depends_on:
- db
volumes:
- ./logs:/app/logs
db:
image: postgres:14
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
- POSTGRES_USER=user
- POSTGRES_PASSWORD=password
- POSTGRES_DB=mydb
volumes:
postgres_data:
# Heroku Procfile
web: gunicorn wsgi:application
# AWS Elastic Beanstalk - requirements.txt
Flask==2.0.1
gunicorn==20.1.0
psycopg2-binary==2.9.1
SQLAlchemy==1.4.23
# .ebextensions/01_flask.config
option_settings:
aws:elasticbeanstalk:container:python:
WSGIPath: wsgi:application
aws:elasticbeanstalk:application:environment:
FLASK_ENV: production
DATABASE_URL: postgresql://user:password@${RDS_HOSTNAME}:${RDS_PORT}/${RDS_DB_NAME}
# GitHub Actions workflow (.github/workflows/deploy.yml)
name: Deploy Python Application
on:
push:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.10'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pytest pytest-cov
pip install -r requirements.txt
- name: Run tests
run: |
pytest --cov=./ --cov-report=xml
- name: Upload coverage reports
uses: codecov/codecov-action@v1
deploy:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Deploy to Heroku
uses: akhileshns/heroku-deploy@v3.12.12
with:
heroku_api_key: ${{ secrets.HEROKU_API_KEY }}
heroku_app_name: ${{ secrets.HEROKU_APP_NAME }}
heroku_email: ${{ secrets.HEROKU_EMAIL }}
These hands-on exercises will help you apply what you've learned by building real-world components for web applications.
Create a simple REST API for a blog with posts and comments using Flask:
from flask import Flask, request, jsonify
app = Flask(__name__)
# In-memory data store
posts = [
{
"id": 1,
"title": "Introduction to Python",
"content": "Python is a versatile programming language...",
"author": "alice",
"comments": [
{"id": 1, "text": "Great post!", "author": "bob"}
]
}
]
# Routes for posts
@app.route('/api/posts', methods=['GET'])
def get_posts():
return jsonify(posts)
@app.route('/api/posts/', methods=['GET'])
def get_post(post_id):
post = next((p for p in posts if p["id"] == post_id), None)
if post:
return jsonify(post)
return jsonify({"error": "Post not found"}), 404
@app.route('/api/posts', methods=['POST'])
def create_post():
if not request.json:
return jsonify({"error": "Invalid request"}), 400
new_post = {
"id": max(p["id"] for p in posts) + 1 if posts else 1,
"title": request.json.get("title", ""),
"content": request.json.get("content", ""),
"author": request.json.get("author", "anonymous"),
"comments": []
}
posts.append(new_post)
return jsonify(new_post), 201
# Routes for comments
@app.route('/api/posts//comments', methods=['GET'])
def get_comments(post_id):
post = next((p for p in posts if p["id"] == post_id), None)
if not post:
return jsonify({"error": "Post not found"}), 404
return jsonify(post["comments"])
@app.route('/api/posts//comments', methods=['POST'])
def add_comment(post_id):
post = next((p for p in posts if p["id"] == post_id), None)
if not post:
return jsonify({"error": "Post not found"}), 404
if not request.json or not "text" in request.json:
return jsonify({"error": "Invalid comment data"}), 400
comment = {
"id": max(c["id"] for c in post["comments"]) + 1 if post["comments"] else 1,
"text": request.json["text"],
"author": request.json.get("author", "anonymous")
}
post["comments"].append(comment)
return jsonify(comment), 201
if __name__ == '__main__':
app.run(debug=True)
Create a web dashboard to visualize website analytics data:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from flask import Flask, render_template, jsonify
import io
import base64
# Create sample data
def generate_sample_data():
dates = pd.date_range(start='2025-01-01', end='2025-04-30')
sources = ['Direct', 'Organic Search', 'Social', 'Referral', 'Email']
data = []
for date in dates:
for source in sources:
visitors = np.random.randint(10, 500)
bounce_rate = np.random.uniform(0.1, 0.8)
data.append({
'date': date,
'source': source,
'visitors': visitors,
'bounce_rate': bounce_rate
})
df = pd.DataFrame(data)
df.to_csv('analytics_data.csv', index=False)
return df
# Process analytics data
def process_analytics(df):
# Daily traffic
daily = df.groupby('date')['visitors'].sum().reset_index()
daily['date'] = daily['date'].dt.strftime('%Y-%m-%d')
# Traffic by source
by_source = df.groupby('source')['visitors'].sum().reset_index()
# Monthly trends
df['month'] = df['date'].dt.strftime('%b %Y')
monthly = df.groupby('month')['visitors'].sum().reset_index()
return {
'daily': daily.to_dict(orient='records'),
'by_source': by_source.to_dict(orient='records'),
'monthly': monthly.to_dict(orient='records')
}
# Flask app
app = Flask(__name__)
@app.route('/')
def index():
return render_template('dashboard.html')
@app.route('/api/analytics')
def analytics_data():
# In a real app, you'd load from a database or file
df = generate_sample_data()
return jsonify(process_analytics(df))
@app.route('/api/chart/daily')
def daily_chart():
df = pd.read_csv('analytics_data.csv')
df['date'] = pd.to_datetime(df['date'])
daily = df.groupby('date')['visitors'].sum().reset_index()
plt.figure(figsize=(10, 6))
plt.plot(daily['date'], daily['visitors'])
plt.title('Daily Website Traffic')
plt.xlabel('Date')
plt.ylabel('Visitors')
plt.tight_layout()
# Convert plot to base64 image
img = io.BytesIO()
plt.savefig(img, format='png')
img.seek(0)
return base64.b64encode(img.getvalue()).decode()
if __name__ == '__main__':
app.run(debug=True)
Build a web scraper that collects content from various sources and displays it in a unified interface:
import requests
from bs4 import BeautifulSoup
import pandas as pd
from flask import Flask, render_template, request
from datetime import datetime
# Scraper functions
def scrape_tech_news():
articles = []
# Example sources (use actual news sites in a real application)
sources = [
{"url": "https://example-tech-news.com", "selector": ".article"},
{"url": "https://another-tech-blog.com", "selector": ".post"}
]
for source in sources:
try:
response = requests.get(source["url"])
soup = BeautifulSoup(response.text, 'html.parser')
for article_elem in soup.select(source["selector"]):
title = article_elem.select_one('.title').text.strip()
link = article_elem.select_one('a')['href']
date_text = article_elem.select_one('.date').text.strip()
# Standardize date format
try:
date = datetime.strptime(date_text, '%B %d, %Y')
except:
date = datetime.now() # Fallback to current date
# Get summary if available
summary_elem = article_elem.select_one('.summary')
summary = summary_elem.text.strip() if summary_elem else ""
articles.append({
"title": title,
"link": link if link.startswith('http') else f"{source['url']}/{link}",
"date": date,
"summary": summary,
"source": source["url"]
})
except Exception as e:
print(f"Error scraping {source['url']}: {e}")
# Convert to DataFrame for easier manipulation
df = pd.DataFrame(articles)
return df
# Flask app
app = Flask(__name__)
@app.route('/')
def index():
# Scrape or load cached data
articles_df = scrape_tech_news()
# Apply filters if provided
source_filter = request.args.get('source')
if source_filter:
articles_df = articles_df[articles_df['source'] == source_filter]
date_filter = request.args.get('date')
if date_filter:
articles_df = articles_df[articles_df['date'].dt.strftime('%Y-%m-%d') == date_filter]
# Sort by date (newest first)
articles_df = articles_df.sort_values('date', ascending=False)
# Convert to list of dicts for template
articles = articles_df.to_dict(orient='records')
# Get unique sources for filter dropdown
sources = articles_df['source'].unique().tolist()
return render_template('news.html',
articles=articles,
sources=sources,
current_source=source_filter,
current_date=date_filter)
if __name__ == '__main__':
app.run(debug=True)
Continue your Python web development journey with these resources: