', methods=['PUT'])
@login_required
def update_post(post_id):
data = request.get_json()
return jsonify({'status': 'updated'})
```
---
## Persistence and ORM
---
### ORM
- Object-Relational Mapping
- Map Python classes to database tables
- Automatically handle database connections and transactions
- Provide a query interface for database operations
---
### How to avoid writing raw SQL?
---
### Use SQLAlchemy
- SQLAlchemy is the most popular ORM for Flask
- Provides a query interface for database operations
- Supports relationships between models, validations and constraints
---
```python
# models.py
from app import db
class User(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))
posts = db.relationship('Post', backref='author', lazy='dynamic')
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
title = db.Column(db.String(100), nullable=False)
content = db.Column(db.Text, nullable=False)
```
---
Key Features:
- Powerful joins and filtering
- Aggregations and grouping
- Eager loading for performance
- Complex subqueries
- Bulk operations
- Window functions
---
### SQLAlchemy vs Raw SQL ๐
Finding Users and Post Count ๐
# SQLAlchemy (the cool way ๐)
result = (db.session.query(
User.username,
func.count(Post.id).label('posts'))
.join(Post)
.group_by(User.username)
.all())
#### SQL (the old-school way ๐ด)
SELECT users.username, COUNT(posts.id) as posts
FROM users
JOIN posts ON users.id = posts.user_id
GROUP BY users.username;
---
Finding Trending Posts ๐ฅ
# SQLAlchemy (making it rain! ๐ซ)
trending = (Post.query
.filter(Post.created_at >= week_ago)
.order_by(Post.views.desc())
.limit(10)
.all())
-- SQL (the classic approach ๐)
SELECT * FROM posts
WHERE created_at >= DATE_SUB(NOW(), INTERVAL 1 WEEK)
ORDER BY views DESC
LIMIT 10;
---
Complex Nested Conditions ๐งฉ
# SQLAlchemy (flexing those Python muscles ๐ช)
posts = (Post.query
.join(User)
.filter(
or_(
and_(Post.is_published == True,
User.is_verified == True),
User.is_admin == True
))
.all())
-- SQL (when you want to feel nostalgic ๐ฌ)
SELECT posts.* FROM posts
JOIN users ON users.id = posts.user_id
WHERE (posts.is_published = TRUE AND users.is_verified = TRUE)
OR users.is_admin = TRUE;
---
#### Why SQLAlchemy Rocks! ๐ธ
-
๐ Type hints and autocomplete
(no more typos in column names!)
-
๐ก๏ธ SQL injection protection
(keeping the bad guys out!)
-
๐งฉ Reusable query components
(DRY code is happy code!)
-
๐ Database agnostic
(switch databases like changing clothes!)
---
How to safely update database schema? ๐ค
---
Use Flask-Migrate and Alembic ๐
-
๐ Version control for database schema
(like git for your database!)
-
๐ค Team collaboration
(no more "works on my machine"!)
-
๐ก๏ธ Safe schema updates
(because YOLO is not a database strategy!)
-
โฎ๏ธ Rollback capability
(oops button included!)
-
๐ Migrations stored in
migrations
directory
-
๐ Use
flask db migrate/upgrade/downgrade
---
---
Database Migration Examples ๐ก
# After model changes
$ flask db migrate -m "Add user table" # Let Alembic do the detective work ๐
# Review generated migration (always double-check! ๐)
$ code migrations/versions/123abc_add_user_table.py
# Apply migration (fingers crossed! ๐ค)
$ flask db upgrade
# Rollback if needed (your safety net! ๐ช)
$ flask db downgrade
---
Best Practices ๐
-
๐ Always review generated migrations
(trust but verify!)
-
๐งช Test migrations on sample data
(test in staging, not in prod!)
-
๐ Include migrations in code review
(four eyes are better than two!)
-
๐ธ Maintain migration history
(like a family photo album for your schema!)
---
## Web app ๐
---
### 1. Separate Frontend (Recommended) โ๏ธ
-
๐ Independent development workflow
(freedom to innovate!)
-
๐ ๏ธ Modern tooling with Vite/Webpack
(blazing fast builds!)
-
๐ฎ Better developer experience
(happy devs = better code!)
-
๐ฏ Clear separation of concerns
(keeping it clean and organized!)
---
#### Development Workflow - vite dev server ๐
- Two servers running in parallel (like a power couple! ๐)
- Flask server for API endpoints ๐
- vite dev server for frontend development โก
- vite dev server helps with hot-reloading (magic! โจ)
- All API calls are proxied to Flask server (sneaky! ๐ต๏ธโโ๏ธ)
---
#### Production Workflow ๐
- Single server setup (keeping it simple! ๐)
- Flask serves both API and static assets (like a boss! ๐ช)
- React app is pre-built and optimized (zoom zoom! ๐๏ธ)
- No development overhead in production (lean and mean! ๐ช)
---
### 2. Flask Serving Frontend ๐
-
๐จ Traditional server-side rendering with jinja2
(old school but reliable!)
-
๐ฆ No frontend build process needed
(simplicity has its charm!)
-
๐ฏ Plain HTML, CSS and JavaScript
(back to basics!)
Drawbacks ๐
-
๐ฆ No hot-reloading or modern tooling
(welcome to the stone age!)
-
๐ซ Harder to deploy and maintain
(more manual work needed)
-
๐คทโโ๏ธ Modern templates expect vite/webpack
(swimming against the tide!)
---
#### Example of a simple jinja2 template
{% block title %}{% endblock %}
{% block content %}{% endblock %}
---
## Concurrency ๐
---
### Python's Async Features ๐ญ
- Introduced in Python 3.5 with async/await syntax (finally! ๐)
- Coroutines: Functions that can pause and resume execution (like a yoga master! ๐งโโ๏ธ)
- Event Loop: Manages multiple coroutines concurrently (like a juggler! ๐คนโโ๏ธ)
- Great for I/O-bound tasks (network, file operations) ๐
- Not helpful for CPU-bound tasks (use multiprocessing) ๐ค
---
### Async Magic in Python โจ
A Simple Async Example ๐ญ
# Let's fetch some data asynchronously! ๐
async def fetch_data():
print("Starting the journey... ๐โโ๏ธ")
await asyncio.sleep(2) # Pretending to do some heavy lifting ๐๏ธโโ๏ธ
print("Mission accomplished! ๐ฏ")
return {"data": "here"}
async def main():
# Launch 3 tasks at once (like a juggler! ๐คนโโ๏ธ)
tasks = [fetch_data() for _ in range(3)]
results = await asyncio.gather(*tasks)
print("All done! ๐")
# Takes ~2s instead of 6s (Speed demon! ๐๏ธ)
asyncio.run(main())
---
### What's happening here? ๐ค
-
โธ๏ธ Tasks can pause and resume
(like hitting the pause button!)
-
๐ Event loop manages everything
(the conductor of our async orchestra!)
-
โก Non-blocking operations
(no more waiting around!)
-
๐ฏ Perfect for I/O operations
(like fetching cat pictures from the internet!)
---
### When to Use Async? ๐ฏ
Great Use Cases ๐
-
๐ Web requests
(API calls galore!)
-
๐พ Database operations
(async all the things!)
-
๐ File operations
(read/write like a ninja!)
Not So Great ๐ซ
-
๐งฎ Heavy calculations
(CPU goes brrrr!)
-
๐ Image processing
(use multiprocessing instead!)
-
๐งฌ Machine learning
(async won't help here!)
---
## Deploying Flask Applications
---
### The Deployment Stack
- Nginx: Reverse proxy, static files, SSL termination
- Gunicorn: Production-grade WSGI server
- Docker: Containerization and environment consistency
---
### Best Practices for Production
- Use environment variables for configuration
- Separate development and production settings
- Handle static files with Nginx, not Flask
- Configure proper logging and monitoring
- Set up health checks
---
### Dockerfile Tips
```dockerfile
FROM python:3.11-slim
# Keep layers minimal and clean
RUN apt-get update && apt-get install -y \
build-essential python3-dev \
&& rm -rf /var/lib/apt/lists/*
# Use multi-stage builds for frontend
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Add healthcheck
HEALTHCHECK CMD curl --fail http://localhost:8080/health
```
---
### Gunicorn Configuration
- Worker Types: sync, gevent, eventlet (choose your poison)
- Worker Count: (2 x CPU cores) + 1
- Timeouts: Request timeout, graceful shutdown
- Logging: Error logs, access logs
```python
# gunicorn-conf.py
import multiprocessing
workers = multiprocessing.cpu_count() * 2 + 1
loglevel = "info"
capture_output = True
```
---
### Nginx as Reverse Proxy
- Handles SSL/TLS termination
- Serves static files efficiently
- Load balancing between workers
- Buffers slow clients
---
### Deployment Checklist
1. Domain & SSL Setup
2. Environment Configuration
3. Monitoring & Logging
4. Database Management
---
#### 1. Domain & SSL Setup
- Purchase cheap domain names at spaceship.com or hostinger.in
- Use chatgpt to brainstorm domain names and ideas
- Helpful tip: Use cloudflare.com as your reverse proxy
- Free SSL certificates
- DDoS protection
- CDN for static assets
- and more...
---
#### 2. Hosting Options for Flask Apps
- Top providers:
- Hostinger (My recommendation, budget friendly and good support)
- DigitalOcean
- Linode/Akamai
- Hetzner
- Pro tip: Start with basic VPS, upgrade when needed
---
#### 3. Environment Configuration
- Use Docker's
.env
file for development
- If you are nervous about handling secrets, use a secret manager
- Secret managers for production:
---
#### 4. Monitoring & Logging
- Start with a simple logging setup, periodically check logs for errors or issues
- For error tracking, start with Sentry.io. It's free and easy to configure flask/python projects
- Health checks and metrics - create a simple health check endpoint in your flask app.
- Can use more sophisticated tools like Prometheus, Grafana, etc later on.
---
#### 5. Database Management
- Choose a database that you are comfortable with. My recommendation, don't overcomplicate: All you need is postgres
- Regular backups to cloud storage
- Automated with cron jobs
- Test restore procedures
- Use connection pooling to manage database connections efficiently.
- Migration strategy - Use Flask-Migrate for database migrations.
---
## Key Takeaways ๐ฏ
- Flask = Simple, flexible, productive (like a Swiss Army knife! ๐ง)
- Sync is fine, scale horizontally (go wide! ๐)
- Flask + Gunicorn + Nginx = Production ready (dream team! ๐)
- Start with VPS + Cloudflare (keep it simple! ๐)
- Postgres + Sentry = Peace of mind (sleep well! ๐ด)
---
## Resources
- Official Flask Documentation: https://flask.palletsprojects.com/
- Flask Mega-Tutorial by Miguel Grinberg
- Full Stack Python: https://www.fullstackpython.com/flask.html
- Docker Documentation: https://docs.docker.com/
- React Documentation: https://react.dev/
---
## Questions? ๐ค
Thank you for attending! ๐
---
## Misc Slides
---
## Common Pitfalls & Solutions
### CORS in Development
```python
# Flask backend
from flask_cors import CORS
app = Flask(__name__)
CORS(app, resources={r"/api/*": {"origins": "http://localhost:3000"}})
```
```js
// Vite/Webpack frontend
export default {
server: {
proxy: {
'/api': 'http://localhost:5000'
}
}
}
```
---
### SQLAlchemy vs Raw SQL
# Find users and their post count
# SQLAlchemy
result = (db.session.query(
User.username,
func.count(Post.id).label('posts'))
.join(Post)
.group_by(User.username)
.all())
# SQL equivalent
"""
SELECT users.username, COUNT(posts.id) as posts
FROM users
JOIN posts ON users.id = posts.user_id
GROUP BY users.username;
"""
---
# Find trending posts (most views this week)
# SQLAlchemy
trending = (Post.query
.filter(Post.created_at >= week_ago)
.order_by(Post.views.desc())
.limit(10)
.all())
# SQL equivalent
"""
SELECT * FROM posts
WHERE created_at >= DATE_SUB(NOW(), INTERVAL 1 WEEK)
ORDER BY views DESC
LIMIT 10;
"""
---
# Complex nested conditions
# SQLAlchemy
posts = (Post.query
.join(User)
.filter(
or_(
and_(Post.is_published == True,
User.is_verified == True),
User.is_admin == True
))
.all())
# SQL equivalent
"""
SELECT posts.* FROM posts
JOIN users ON users.id = posts.user_id
WHERE (posts.is_published = TRUE AND users.is_verified = TRUE)
OR users.is_admin = TRUE;
"""