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:

  1. ✅ Set DEBUG = False

  2. ✅ Configure ALLOWED_HOSTS

  3. ✅ Set a strong SECRET_KEY via environment variable

  4. ✅ Use PostgreSQL instead of SQLite

  5. ✅ Set up Gunicorn as WSGI server

  6. ✅ Configure Nginx as reverse proxy

  7. ✅ Set up static/media file serving

  8. ✅ Implement proper database indexing

  9. ✅ Add caching layer

  10. ✅ 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!

LecturreAI Assistant

Hello! I'm LecturreAI

What would you like to learn today?