Learn Django deployment strategies, performance optimization, production settings, and best practices for web development.
Learn Django deployment strategies, performance optimization, production settings, and best practices for web development.
Learn how to configure Django settings for secure and performant production environments.
Content by: Manali Trivedi
Python Django Developer
Django applications need different settings for production environments. Understanding production configuration is crucial for deploying secure and performant applications.
# settings/production.py
import os
from .base import *
# Security settings
DEBUG = False
ALLOWED_HOSTS = ['yourdomain.com', 'www.yourdomain.com']
# Database
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': os.environ.get('DB_NAME'),
'USER': os.environ.get('DB_USER'),
'PASSWORD': os.environ.get('DB_PASSWORD'),
'HOST': os.environ.get('DB_HOST'),
'PORT': os.environ.get('DB_PORT', '5432'),
}
}
# Static files
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.ManifestStaticFilesStorage'
# Media files
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'
# Logging
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'file': {
'level': 'INFO',
'class': 'logging.FileHandler',
'filename': '/var/log/django/app.log',
},
},
'loggers': {
'django': {
'handlers': ['file'],
'level': 'INFO',
'propagate': True,
},
},
}
# Cache
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.redis.RedisCache',
'LOCATION': 'redis://127.0.0.1:6379/1',
}
}
# Email
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = os.environ.get('EMAIL_HOST')
EMAIL_PORT = os.environ.get('EMAIL_PORT', 587)
EMAIL_USE_TLS = True
EMAIL_HOST_USER = os.environ.get('EMAIL_HOST_USER')
EMAIL_HOST_PASSWORD = os.environ.get('EMAIL_HOST_PASSWORD')
# Security
SECURE_SSL_REDIRECT = True
SECURE_HSTS_SECONDS = 31536000
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = TrueTest your understanding of this topic:
Explore various platforms and strategies for deploying Django applications.
Content by: Manali Trivedi
Python Django Developer
There are multiple ways to deploy Django applications. Each platform has its advantages and considerations for different use cases.
# Heroku Deployment
# requirements.txt
Django==4.2.0
gunicorn==20.1.0
psycopg2-binary==2.9.6
whitenoise==6.4.0
# Procfile
web: gunicorn myproject.wsgi --log-file -
# Runtime.txt
python-3.11.0
# Heroku commands
heroku create my-django-app
heroku addons:create heroku-postgresql:hobby-dev
heroku config:set SECRET_KEY=your-secret-key
heroku config:set DEBUG=False
git push heroku main
heroku run python manage.py migrate
# DigitalOcean App Platform
# app.yaml
name: django-app
services:
- name: web
source_dir: /
github:
repo: username/django-app
branch: main
run_command: gunicorn myproject.wsgi:application
environment_slug: python
instance_count: 1
instance_size_slug: basic-xxs
# AWS Elastic Beanstalk
# .ebextensions/django.config
option_settings:
aws:elasticbeanstalk:container:python:
WSGIPath: myproject.wsgi:application
aws:elasticbeanstalk:environment:proxy:staticfiles:
/static: static
# Docker Deployment
# Dockerfile
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
RUN python manage.py collectstatic --noinput
EXPOSE 8000
CMD ["gunicorn", "myproject.wsgi:application", "--bind", "0.0.0.0:8000"]
# docker-compose.yml
version: '3.8'
services:
web:
build: .
ports:
- "8000:8000"
environment:
- DEBUG=False
- DATABASE_URL=postgresql://user:pass@db:5432/dbname
depends_on:
- db
db:
image: postgres:13
environment:
- POSTGRES_DB=dbname
- POSTGRES_USER=user
- POSTGRES_PASSWORD=pass
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:Test your understanding of this topic:
Learn tools and practices for maintaining high code quality in Django projects.
Content by: Manali Trivedi
Python Django Developer
Maintaining high code quality is essential for long-term project success. Various tools help enforce coding standards and identify potential issues.
# Code Quality Tools Setup
# requirements-dev.txt (Development dependencies)
flake8==6.0.0
black==23.3.0
isort==5.12.0
pylint==2.17.4
mypy==1.3.0
bandit==1.7.5
pre-commit==3.3.2
# .flake8 configuration
[flake8]
max-line-length = 88
extend-ignore = E203, W503
exclude =
.git,
__pycache__,
.venv,
venv,
env,
migrations,
staticfiles,
media
# Black configuration (pyproject.toml)
[tool.black]
line-length = 88
target-version = ['py39']
include = '\.pyi?$'
extend-exclude = '''
/(
# directories
\.eggs
| \.git
| \.hg
| \.mypy_cache
| \.tox
| \.venv
| _build
| buck-out
| build
| dist
)/
'''
# isort configuration (pyproject.toml)
[tool.isort]
profile = "black"
multi_line_output = 3
line_length = 88
known_django = "django"
known_first_party = "myapp"
sections = ["FUTURE", "STDLIB", "DJANGO", "THIRDPARTY", "FIRSTPARTY", "LOCALFOLDER"]
# Pylint configuration (.pylintrc)
[MASTER]
disable=
C0114, # missing-module-docstring
C0115, # missing-class-docstring
C0116, # missing-function-docstring
[FORMAT]
max-line-length=88
[MESSAGES CONTROL]
disable=
C0103, # invalid-name
C0301, # line-too-long
R0903, # too-few-public-methods
R0913, # too-many-arguments
# MyPy configuration (pyproject.toml)
[tool.mypy]
python_version = "3.9"
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = true
disallow_incomplete_defs = true
check_untyped_defs = true
disallow_untyped_decorators = true
no_implicit_optional = true
warn_redundant_casts = true
warn_unused_ignores = true
warn_no_return = true
warn_unreachable = true
strict_equality = true
[[tool.mypy.overrides]]
module = [
"django.*",
"django_extensions.*",
]
ignore_missing_imports = true
# Pre-commit hooks (.pre-commit-config.yaml)
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
- id: check-merge-conflict
- repo: https://github.com/psf/black
rev: 23.3.0
hooks:
- id: black
language_version: python3
- repo: https://github.com/pycqa/isort
rev: 5.12.0
hooks:
- id: isort
- repo: https://github.com/pycqa/flake8
rev: 6.0.0
hooks:
- id: flake8
- repo: https://github.com/pycqa/bandit
rev: 1.7.5
hooks:
- id: bandit
args: [-r, ., -f, json, -o, bandit-report.json]
# Custom Django management command for code quality
# management/commands/check_code_quality.py
from django.core.management.base import BaseCommand
import subprocess
import sys
class Command(BaseCommand):
help = 'Check code quality using various tools'
def add_arguments(self, parser):
parser.add_argument(
'--fix',
action='store_true',
help='Automatically fix formatting issues'
)
def handle(self, *args, **options):
tools = [
('Black', ['black', '--check', '.']),
('isort', ['isort', '--check-only', '.']),
('Flake8', ['flake8', '.']),
('Pylint', ['pylint', 'myapp/']),
('MyPy', ['mypy', '.']),
('Bandit', ['bandit', '-r', '.']),
]
failed_tools = []
for tool_name, command in tools:
self.stdout.write(f'Running {tool_name}...')
try:
result = subprocess.run(
command,
capture_output=True,
text=True
)
if result.returncode == 0:
self.stdout.write(
self.style.SUCCESS(f'{tool_name}: PASSED')
)
else:
self.stdout.write(
self.style.ERROR(f'{tool_name}: FAILED')
)
self.stdout.write(result.stdout)
self.stdout.write(result.stderr)
failed_tools.append(tool_name)
except FileNotFoundError:
self.stdout.write(
self.style.WARNING(f'{tool_name}: Not installed')
)
if failed_tools:
self.stdout.write(
self.style.ERROR(
f'Code quality checks failed for: {", ".join(failed_tools)}'
)
)
sys.exit(1)
else:
self.stdout.write(
self.style.SUCCESS('All code quality checks passed!')
)
# Usage: python manage.py check_code_quality
# GitHub Actions workflow for code quality
# .github/workflows/code-quality.yml
name: Code Quality
on: [push, pull_request]
jobs:
quality:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.9'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements-dev.txt
- name: Run Black
run: black --check .
- name: Run isort
run: isort --check-only .
- name: Run Flake8
run: flake8 .
- name: Run Pylint
run: pylint myapp/
- name: Run MyPy
run: mypy .
- name: Run Bandit
run: bandit -r . -f json -o bandit-report.json
# IDE Configuration (VS Code)
# .vscode/settings.json
{
"python.linting.enabled": true,
"python.linting.flake8Enabled": true,
"python.linting.pylintEnabled": true,
"python.linting.mypyEnabled": true,
"python.formatting.provider": "black",
"python.sortImports.args": ["--profile", "black"],
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports": true
},
"python.linting.flake8Args": ["--max-line-length=88"],
"python.linting.pylintArgs": ["--max-line-length=88"]
}Test your understanding of this topic:
Learn techniques to optimize Django applications for better performance in production.
Content by: Manali Trivedi
Python Django Developer
Performance optimization is crucial for production applications. Django provides many tools and techniques to improve application performance.
# Database Optimization
# Use select_related for ForeignKey
posts = Post.objects.select_related('author', 'category').all()
# Use prefetch_related for ManyToMany
articles = Article.objects.prefetch_related('tags').all()
# Use only() to limit fields
posts = Post.objects.only('title', 'created_date').all()
# Use defer() to exclude fields
posts = Post.objects.defer('content').all()
# Database indexing
class Post(models.Model):
title = models.CharField(max_length=200, db_index=True)
created_date = models.DateTimeField(auto_now_add=True, db_index=True)
class Meta:
indexes = [
models.Index(fields=['author', 'created_date']),
models.Index(fields=['category', 'published_date']),
]
# Caching
from django.core.cache import cache
from django.views.decorators.cache import cache_page
# View-level caching
@cache_page(60 * 15) # Cache for 15 minutes
def expensive_view(request):
# Expensive computation
return render(request, 'template.html')
# Template fragment caching
{% load cache %}
{% cache 500 sidebar request.user.username %}
<!-- Expensive sidebar content -->
{% endcache %}
# Low-level caching
def get_user_posts(user_id):
cache_key = f'user_posts_{user_id}'
posts = cache.get(cache_key)
if posts is None:
posts = Post.objects.filter(author_id=user_id)
cache.set(cache_key, posts, 300) # Cache for 5 minutes
return posts
# Database connection optimization
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'mydb',
'CONN_MAX_AGE': 60, # Keep connections alive for 60 seconds
'OPTIONS': {
'MAX_CONNS': 20, # Maximum connections
}
}
}
# Static files optimization
STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.ManifestStaticFilesStorage'
# Template optimization
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'OPTIONS': {
'loaders': [
('django.template.loaders.cached.Loader', [
'django.template.loaders.filesystem.Loader',
'django.template.loaders.app_directories.Loader',
]),
],
},
},
]
# Middleware optimization
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'whitenoise.middleware.WhiteNoiseMiddleware', # Serve static files
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
# Query optimization
from django.db.models import Count, Prefetch
# Optimize complex queries
posts = Post.objects.select_related('author').prefetch_related(
Prefetch('comments', queryset=Comment.objects.select_related('author'))
).annotate(
comment_count=Count('comments')
)
# Use bulk operations
# Bulk create
Post.objects.bulk_create([
Post(title=f'Post {i}', content=f'Content {i}')
for i in range(1000)
])
# Bulk update
Post.objects.filter(is_featured=True).update(
featured_date=timezone.now()
)Test your understanding of this topic:
Learn how to monitor and maintain Django applications in production for reliability and performance.
Content by: Manali Trivedi
Python Django Developer
Monitoring and maintaining Django applications in production is essential for ensuring reliability and performance.
# Django Debug Toolbar (for development)
INSTALLED_APPS = [
# ...
'debug_toolbar',
]
MIDDLEWARE = [
'debug_toolbar.middleware.DebugToolbarMiddleware',
# ... other middleware
]
INTERNAL_IPS = [
'127.0.0.1',
]
# Custom management commands
# management/commands/check_site_health.py
from django.core.management.base import BaseCommand
from django.core.cache import cache
from django.db import connection
class Command(BaseCommand):
help = 'Check site health and performance'
def handle(self, *args, **options):
# Check database connection
try:
with connection.cursor() as cursor:
cursor.execute("SELECT 1")
self.stdout.write(self.style.SUCCESS('Database: OK'))
except Exception as e:
self.stdout.write(self.style.ERROR(f'Database: ERROR - {e}'))
# Check cache
try:
cache.set('health_check', 'ok', 10)
if cache.get('health_check') == 'ok':
self.stdout.write(self.style.SUCCESS('Cache: OK'))
else:
self.stdout.write(self.style.ERROR('Cache: ERROR'))
except Exception as e:
self.stdout.write(self.style.ERROR(f'Cache: ERROR - {e}'))
# Error tracking with Sentry
import sentry_sdk
from sentry_sdk.integrations.django import DjangoIntegration
sentry_sdk.init(
dsn="your-sentry-dsn",
integrations=[DjangoIntegration()],
traces_sample_rate=1.0,
send_default_pii=True
)
# Custom logging
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'verbose': {
'format': '{levelname} {asctime} {module} {process:d} {thread:d} {message}',
'style': '{',
},
},
'handlers': {
'file': {
'level': 'INFO',
'class': 'logging.FileHandler',
'filename': '/var/log/django/app.log',
'formatter': 'verbose',
},
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'formatter': 'verbose',
},
},
'loggers': {
'django': {
'handlers': ['file', 'console'],
'level': 'INFO',
'propagate': True,
},
'myapp': {
'handlers': ['file', 'console'],
'level': 'DEBUG',
'propagate': True,
},
},
}
# Performance monitoring
from django.core.cache import cache
import time
def performance_monitor(func):
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
execution_time = end_time - start_time
cache_key = f'performance_{func.__name__}'
# Store performance metrics
cache.set(cache_key, execution_time, 3600)
return result
return wrapper
# Database backup
# management/commands/backup_db.py
from django.core.management.base import BaseCommand
from django.conf import settings
import subprocess
import os
class Command(BaseCommand):
help = 'Backup database'
def handle(self, *args, **options):
db_settings = settings.DATABASES['default']
if db_settings['ENGINE'] == 'django.db.backends.postgresql':
# PostgreSQL backup
backup_file = f'backup_{timezone.now().strftime("%Y%m%d_%H%M%S")}.sql'
subprocess.run([
'pg_dump',
'-h', db_settings['HOST'],
'-U', db_settings['USER'],
'-d', db_settings['NAME'],
'-f', backup_file
])
self.stdout.write(self.style.SUCCESS(f'Backup created: {backup_file}'))
# Health check endpoint
from django.http import JsonResponse
from django.views.decorators.http import require_http_methods
@require_http_methods(["GET"])
def health_check(request):
try:
# Check database
from django.db import connection
with connection.cursor() as cursor:
cursor.execute("SELECT 1")
# Check cache
cache.set('health_check', 'ok', 10)
cache_ok = cache.get('health_check') == 'ok'
if cache_ok:
return JsonResponse({
'status': 'healthy',
'database': 'ok',
'cache': 'ok'
})
else:
return JsonResponse({
'status': 'unhealthy',
'cache': 'error'
}, status=500)
except Exception as e:
return JsonResponse({
'status': 'unhealthy',
'error': str(e)
}, status=500)Test your understanding of this topic:
Continue your learning journey and master the next set of concepts.
Back to Course Overview