Django & Flask Topic
Django Development vs. Production
-
Development: Your local machine with
runserver, SQLite,DEBUG=True -
Production: A live server on the internet with proper security, performance, and reliability
The transition requires significant changes to make your app secure, fast, and scalable.
1. Deployment: Going Live
Production Environment Setup
Django's runserver is for development only. In production, you need a proper web server setup:
1. Web Server Gateway Interface (WSGI)
Django uses WSGI to communicate with web servers. The most common setup is:
python
# myproject/wsgi.py
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')
application = get_wsgi_application()
2. Gunicorn (Green Unicorn) - Python WSGI Server
bash
pip install gunicorn
Run Django with Gunicorn:
bash
gunicorn myproject.wsgi:application
3. Nginx - Web Server/Reverse Proxy
Nginx handles:
-
Serving static files efficiently
-
SSL/TLS termination
-
Load balancing
-
Reverse proxying to Gunicorn
Typical Architecture:
text
Internet User → Nginx → Gunicorn → Django
Serving Static and Media Files
In development, runserver serves static files. In production, you need a proper solution.
1. Collect Static Files
bash
python manage.py collectstatic
This gathers all static files from all apps into one directory (STATIC_ROOT).
2. WhiteNoise - Serving Static Files
bash
pip install whitenoise
python
# settings.py
MIDDLEWARE = [
'whitenoise.middleware.WhiteNoiseMiddleware', # Add this
# ...
]
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
# For media files (user-uploaded content)
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
For production, also configure Nginx:
nginx
# nginx.conf
server {
location /static/ {
alias /path/to/your/staticfiles/;
}
location /media/ {
alias /path/to/your/media/;
}
}
Environment Variables for Security
Never hardcode sensitive information! Use environment variables.
1. Install python-decouple:
bash
pip install python-decouple
2. Create a .env file (development):
ini
# .env (add to .gitignore!) DEBUG=True SECRET_KEY=your-super-secret-key-here DATABASE_URL=sqlite:///db.sqlite3 ALLOWED_HOSTS=localhost,127.0.0.1
3. Update settings.py:
python
# settings.py
from decouple import config
SECRET_KEY = config('SECRET_KEY')
DEBUG = config('DEBUG', default=False, cast=bool)
ALLOWED_HOSTS = config('ALLOWED_HOSTS', cast=lambda v: [s.strip() for s in v.split(',')])
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
# For production, use a proper database
if not DEBUG:
import dj_database_url
DATABASES['default'] = dj_database_url.config(
default=config('DATABASE_URL'),
conn_max_age=600,
ssl_require=True
)
Production Checklist
Critical Security Settings:
python
# settings.py
if not DEBUG:
# Security settings
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_BROWSER_XSS_FILTER = True
SECURE_CONTENT_TYPE_NOSNIFF = True
# Heroku and other platforms often set this
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
2. Performance Optimization
Database Optimization
1. Use PostgreSQL (Production Database)
python
# settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'mydatabase',
'USER': 'mydatabaseuser',
'PASSWORD': config('DB_PASSWORD'),
'HOST': 'localhost',
'PORT': '5432',
}
}
2. Database Indexing
python
# models.py
class BlogPost(models.Model):
title = models.CharField(max_length=200, db_index=True) # Add index
published_date = models.DateTimeField(db_index=True) # Add index
author = models.ForeignKey(Author, on_delete=models.CASCADE)
class Meta:
indexes = [
models.Index(fields=['published_date', 'author']), # Composite index
]
3. select_related() - For Foreign Key Relationships
Reduces database queries for forward relationships (OneToOne, ForeignKey).
python
# BAD: Causes N+1 query problem
posts = BlogPost.objects.all()
for post in posts:
print(post.author.name) # New query for each post!
# GOOD: Uses JOIN in single query
posts = BlogPost.objects.select_related('author').all()
for post in posts:
print(post.author.name) # No additional queries
4. prefetch_related() - For Many-to-Many and Reverse Relationships
Reduces queries for many-to-many and reverse foreign key relationships.
python
# BAD: Multiple queries for categories
posts = BlogPost.objects.all()
for post in posts:
print([cat.name for cat in post.categories.all()]) # New query each time!
# GOOD: Prefetches in two queries
posts = BlogPost.objects.prefetch_related('categories').all()
for post in posts:
print([cat.name for cat in post.categories.all()]) # Uses prefetched data
5. only() and defer() - Control Field Selection
python
# Only fetch specific fields
posts = BlogPost.objects.only('title', 'published_date')
# SELECT title, published_date FROM blog_post
# Defer large fields until needed
posts = BlogPost.objects.defer('content') # Don't load content initially
6. Django Debug Toolbar
Essential for identifying performance issues in development:
bash
pip install django-debug-toolbar
python
# settings.py
if DEBUG:
INSTALLED_APPS += ['debug_toolbar']
MIDDLEWARE += ['debug_toolbar.middleware.DebugToolbarMiddleware']
INTERNAL_IPS = ['127.0.0.1']
Caching Strategies
1. Per-view Caching
python
# views.py
from django.views.decorators.cache import cache_page
@cache_page(60 * 15) # Cache for 15 minutes
def blog_post_list(request):
posts = BlogPost.objects.all()
return render(request, 'blog/post_list.html', {'posts': posts})
# In urls.py
from django.views.decorators.cache import cache_page
urlpatterns = [
path('posts/', cache_page(60 * 15)(BlogPostList.as_view())),
]
2. Template Fragment Caching
html
{% load cache %}
<!-- Cache this fragment for 10 minutes -->
{% cache 600 sidebar %}
<div class="sidebar">
<h3>Recent Posts</h3>
{% for post in recent_posts %}
<p>{{ post.title }}</p>
{% endfor %}
</div>
{% endcache %}
3. Database Caching Backend
python
# settings.py
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.redis.RedisCache',
'LOCATION': config('REDIS_URL', default='redis://127.0.0.1:6379'),
}
}
Other Performance Tips
1. Use Paginator for Large Querysets
python
from django.core.paginator import Paginator
def post_list(request):
post_list = BlogPost.objects.all()
paginator = Paginator(post_list, 25) # 25 posts per page
page_number = request.GET.get('page')
page_obj = paginator.get_page(page_number)
return render(request, 'blog/list.html', {'page_obj': page_obj})
2. Database Connection Pooling
python
# settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
# ...
'CONN_MAX_AGE': 600, # Keep connections for 10 minutes
}
}
3. Use values() and values_list() for Specific Data
python
x
# settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
# ...
'CONN_MAX_AGE': 600, # Keep connections for 10 minutes
}
}
Deployment Platforms
1. Platform as a Service (PaaS) - Easiest
-
Heroku: Great for beginners
-
Railway: Modern alternative
-
PythonAnywhere: Simple hosting
2. Virtual Private Server (VPS) - More Control
-
DigitalOcean: Popular and affordable
-
AWS EC2: Enterprise-grade
-
Linode: Good performance
3. Container Platforms
-
Docker: Package your app consistently
-
Kubernetes: For scaling complex applications
Summary
| Category | Tool/Technique | Purpose |
|---|---|---|
| Deployment | Gunicorn + Nginx | Production web server setup |
| Static Files | WhiteNoise + CDN | Efficient static file serving |
| Security | Environment Variables | Protect sensitive configuration |
| Database | PostgreSQL | Production-grade database |
| Query Opt | select_related() |
Optimize foreign key queries |
| Query Opt | prefetch_related() |
Optimize many-to-many queries |
| Query Opt | only()/defer() |
Control field selection |
| Caching | Redis + cache_page |
Improve response times |
| Monitoring | Django Debug Toolbar | Identify performance issues |
Production Deployment Checklist:
-
✅ Set
DEBUG = False -
✅ Configure
ALLOWED_HOSTS -
✅ Set a strong
SECRET_KEYvia environment variable -
✅ Use PostgreSQL instead of SQLite
-
✅ Set up Gunicorn as WSGI server
-
✅ Configure Nginx as reverse proxy
-
✅ Set up static/media file serving
-
✅ Implement proper database indexing
-
✅ Add caching layer
-
✅ Set up monitoring and error tracking
By following these practices, you'll have a secure, performant Django application ready for production traffic!
Comments (0)
No comments yet. Be the first to comment!