Django Fundamentals

This chapter delves into Django's core concepts, including project setup, application configuration, middleware systems, signal mechanisms, and more, to give you a comprehensive understanding of how the Django framework works.

Django Project Architecture

MVT Architecture Pattern

Django employs the MVT (Model-View-Template) architectural pattern, a variant of the traditional MVC pattern:

┌─────────────────────────────────────────────────────────┐
│                    Django MVT Architecture               │
├─────────────────────────────────────────────────────────┤
│  HTTP Request → URL Routing → View → Model              │
│                        ↓              ↓                 │
│                    Template ← Data Processing           │
│                        ↓                                │
│                   HTTP Response → Client                │
└─────────────────────────────────────────────────────────┘

Model (模型):

  • Defines data structures and business logic
  • Interacts with the database
  • Data validation and processing

View (视图):

  • Handles HTTP requests
  • Controls business logic
  • Calls models and templates

Template (模板):

  • Presentation layer logic
  • HTML generation
  • Data display

Request-Response Flow

# 1. URL routing match
# urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('articles/<int:id>/', views.article_detail, name='article_detail'),
]

# 2. View processes the request
# views.py
from django.shortcuts import render, get_object_or_404
from .models import Article

def article_detail(request, id):
    article = get_object_or_404(Article, id=id)
    return render(request, 'articles/detail.html', {'article': article})

# 3. Model data query
# models.py
from django.db import models

class Article(models.Model):
    title = models.CharField(max_length=200)
    content = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)
    
    def __str__(self):
        return self.title

# 4. Template rendering
# templates/articles/detail.html
<h1>{{ article.title }}</h1>
<p>{{ article.content }}</p>
<small>Published: {{ article.created_at }}</small>

Django Settings System

Settings File Structure

# Basic structure of settings.py
import os
from pathlib import Path

# Project root directory
BASE_DIR = Path(__file__).resolve().parent.parent

# Core settings
DEBUG = True
SECRET_KEY = 'your-secret-key'
ALLOWED_HOSTS = []

# Applications
INSTALLED_APPS = [
    # Django built-in applications
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    
    # Third-party applications
    'rest_framework',
    'debug_toolbar',
    
    # Local applications
    'blog.apps.BlogConfig',
    'accounts.apps.AccountsConfig',
]

# Middleware
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    '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',
]

# URL configuration
ROOT_URLCONF = 'mysite.urls'

# Database
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}
```##

# Important Settings Explained

#### DEBUG Setting

```python
# Development environment
DEBUG = True
ALLOWED_HOSTS = []  # Empty list means only localhost allowed

# Production environment
DEBUG = False
ALLOWED_HOSTS = ['yourdomain.com', 'www.yourdomain.com']

# Features when DEBUG=True
- Detailed error pages
- Automatic code reloading
- Static files served automatically
- SQL query debugging information

# Requirements when DEBUG=False
- MUST set ALLOWED_HOSTS
- Need to configure static file serving
- Error messages are hidden
- Performance optimizations enabled

SECRET_KEY Setting

# Generate a secure secret key
from django.core.management.utils import get_random_secret_key
SECRET_KEY = get_random_secret_key()

# Read from environment variable
import os
SECRET_KEY = os.environ.get('SECRET_KEY')

# Use python-decouple
from decouple import config
SECRET_KEY = config('SECRET_KEY')

# Uses of SECRET_KEY
- CSRF token generation
- Session signing
- Password reset tokens
- Other cryptographic operations

Database Configuration

# SQLite configuration (development environment)
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}

# PostgreSQL configuration (recommended for production)
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'mydatabase',
        'USER': 'mydatabaseuser',
        'PASSWORD': 'mypassword',
        'HOST': '127.0.0.1',
        'PORT': '5432',
        'OPTIONS': {
            'init_command': "SET sql_mode='STRICT_TRANS_TABLES'",
        },
    }
}

# MySQL configuration
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'mydatabase',
        'USER': 'mydatabaseuser',
        'PASSWORD': 'mypassword',
        'HOST': '127.0.0.1',
        'PORT': '3306',
        'OPTIONS': {
            'init_command': "SET sql_mode='STRICT_TRANS_TABLES'",
        },
    }
}

# Multiple database configuration
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'main_db',
        'USER': 'postgres',
        'PASSWORD': 'password',
        'HOST': 'localhost',
        'PORT': '5432',
    },
    'users_db': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'users_db',
        'USER': 'mysql_user',
        'PASSWORD': 'mysql_password',
        'HOST': 'localhost',
        'PORT': '3306',
    }
}

# Database routing
DATABASE_ROUTERS = ['mysite.routers.DatabaseRouter']

Application Configuration System

AppConfig Class

# apps.py
from django.apps import AppConfig

class BlogConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'blog'
    verbose_name = 'Blog Management'
    
    def ready(self):
        """Called when the application is ready"""
        # Import signal handlers
        import blog.signals
        
        # Execute initialization code
        self.setup_logging()
    
    def setup_logging(self):
        """Set up logging configuration"""
        import logging
        logger = logging.getLogger(__name__)
        logger.info(f'{self.verbose_name} application loaded')

Application Registration

# settings.py
INSTALLED_APPS = [
    # Using AppConfig class
    'blog.apps.BlogConfig',
    
    # Or simplified form
    'blog',
    
    # Third-party applications
    'rest_framework',
    'corsheaders',
    
    # Django built-in applications
    'django.contrib.admin',
    'django.contrib.auth',
]

Application Discovery and Loading

# Django application loading process
1. Read INSTALLED_APPS setting
2. Import each application's AppConfig
3. Call AppConfig.ready() method
4. Register models, management commands, etc.

# Check installed applications
from django.apps import apps

# Get all applications
all_apps = apps.get_app_configs()
for app in all_apps:
    print(f"Application: {app.name}, Label: {app.label}")

# Get specific application
blog_app = apps.get_app_config('blog')
print(f"Application name: {blog_app.verbose_name}")

# Get application's models
blog_models = blog_app.get_models()
for model in blog_models:
    print(f"Model: {model.__name__}")

Middleware System

Middleware Concept

Middleware is Django's hook framework for request/response processing. It's a lightweight plugin system for globally altering Django's input or output.

# Middleware execution order
Request phase (top to bottom):
SecurityMiddleware
SessionMiddleware  
CommonMiddleware
CsrfViewMiddleware
AuthenticationMiddleware
MessageMiddleware
XFrameOptionsMiddleware

  View processing

Response phase (bottom to top):
XFrameOptionsMiddleware
MessageMiddleware
AuthenticationMiddleware
CsrfViewMiddleware
CommonMiddleware
SessionMiddleware
SecurityMiddleware

Built-in Middleware

# settings.py
MIDDLEWARE = [
    # Security middleware - adds security-related HTTP headers
    'django.middleware.security.SecurityMiddleware',
    
    # Session middleware - enables session support
    'django.contrib.sessions.middleware.SessionMiddleware',
    
    # Common middleware - handles URL rewriting, ETags, etc.
    'django.middleware.common.CommonMiddleware',
    
    # CSRF protection middleware
    'django.middleware.csrf.CsrfViewMiddleware',
    
    # Authentication middleware - adds user attribute to request
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    
    # Message middleware - enables message framework
    'django.contrib.messages.middleware.MessageMiddleware',
    
    # Clickjacking protection middleware
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

Custom Middleware

# middleware.py
import time
import logging
from django.utils.deprecation import MiddlewareMixin

class RequestLoggingMiddleware:
    """Request logging middleware"""
    
    def __init__(self, get_response):
        self.get_response = get_response
        self.logger = logging.getLogger(__name__)
    
    def __call__(self, request):
        # Before request processing
        start_time = time.time()
        self.logger.info(f"Request started: {request.method} {request.path}")
        
        # Call next middleware or view
        response = self.get_response(request)
        
        # After response processing
        duration = time.time() - start_time
        self.logger.info(f"Request completed: {response.status_code} ({duration:.2f}s)")
        
        return response
    
    def process_exception(self, request, exception):
        """Handle exceptions"""
        self.logger.error(f"Request exception: {request.path} - {exception}")
        return None

class CustomHeaderMiddleware:
    """Custom HTTP header middleware"""
    
    def __init__(self, get_response):
        self.get_response = get_response
    
    def __call__(self, request):
        response = self.get_response(request)
        
        # Add custom headers
        response['X-Custom-Header'] = 'MyValue'
        response['X-Request-ID'] = getattr(request, 'request_id', 'unknown')
        
        return response

# Class-based middleware (old-style)
class LegacyMiddleware(MiddlewareMixin):
    def process_request(self, request):
        """Process request"""
        request.custom_attribute = 'custom_value'
        return None
    
    def process_response(self, request, response):
        """Process response"""
        response['X-Legacy-Header'] = 'legacy_value'
        return response

Middleware Configuration and Usage

# settings.py
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'myapp.middleware.RequestLoggingMiddleware',  # Custom middleware
    'django.contrib.sessions.middleware.SessionMiddleware',
    'myapp.middleware.CustomHeaderMiddleware',    # Custom middleware
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

# Conditional middleware
if DEBUG:
    MIDDLEWARE += ['debug_toolbar.middleware.DebugToolbarMiddleware']

Signal System

Django Signal Concept

Django signals are a dispatcher allowing decoupled applications to get notified when certain actions occur elsewhere in the framework.

# Common Django signals
from django.db.models.signals import (
    pre_save, post_save,           # Before/after saving
    pre_delete, post_delete,       # Before/after deleting
    m2m_changed,                   # Many-to-many relation changes
)
from django.contrib.auth.signals import (
    user_logged_in,                # User logged in
    user_logged_out,               # User logged out
    user_login_failed,             # Login failed
)
from django.core.signals import (
    request_started,               # Request started
    request_finished,              # Request finished
)

Signal Handlers

# signals.py
from django.db.models.signals import post_save, pre_delete
from django.contrib.auth.signals import user_logged_in
from django.contrib.auth.models import User
from django.dispatch import receiver
from .models import Profile, Article

@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
    """Automatically create user profile when user is created"""
    if created:
        Profile.objects.create(user=instance)
        print(f"Created profile for user {instance.username}")

@receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
    """Save user profile when user is saved"""
    if hasattr(instance, 'profile'):
        instance.profile.save()

@receiver(user_logged_in)
def log_user_login(sender, request, user, **kwargs):
    """Log user login"""
    import logging
    logger = logging.getLogger(__name__)
    logger.info(f"User {user.username} logged in from {request.META.get('REMOTE_ADDR')}")

@receiver(pre_delete, sender=Article)
def backup_article_before_delete(sender, instance, **kwargs):
    """Backup article before deletion"""
    import json
    backup_data = {
        'title': instance.title,
        'content': instance.content,
        'created_at': instance.created_at.isoformat(),
    }
    
    # Save to backup file or database
    with open(f'backup_article_{instance.id}.json', 'w') as f:
        json.dump(backup_data, f)

# Manual signal connection (without decorator)
def article_saved_handler(sender, instance, created, **kwargs):
    if created:
        print(f"New article created: {instance.title}")
    else:
        print(f"Article updated: {instance.title}")

post_save.connect(article_saved_handler, sender=Article)

Custom Signals

# signals.py
import django.dispatch

# Define custom signals
article_published = django.dispatch.Signal()
user_profile_updated = django.dispatch.Signal()

# Sending signals in models
# models.py
from django.db import models
from .signals import article_published

class Article(models.Model):
    title = models.CharField(max_length=200)
    content = models.TextField()
    is_published = models.BooleanField(default=False)
    
    def publish(self):
        self.is_published = True
        self.save()
        
        # Send custom signal
        article_published.send(
            sender=self.__class__,
            instance=self,
            user=self.author
        )

# Handle custom signals
@receiver(article_published)
def notify_subscribers(sender, instance, user, **kwargs):
    """Notify subscribers when an article is published"""
    print(f"Article '{instance.title}' published, author: {user}")
    # Send email notification, push messages, etc.

Logging System

Logging Configuration

# settings.py
import os

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'verbose': {
            'format': '{levelname} {asctime} {module} {process:d} {thread:d} {message}',
            'style': '{',
        },
        'simple': {
            'format': '{levelname} {message}',
            'style': '{',
        },
    },
    'filters': {
        'require_debug_true': {
            '()': 'django.utils.log.RequireDebugTrue',
        },
    },
    'handlers': {
        'console': {
            'level': 'INFO',
            'filters': ['require_debug_true'],
            'class': 'logging.StreamHandler',
            'formatter': 'simple'
        },
        'file': {
            'level': 'INFO',
            'class': 'logging.FileHandler',
            'filename': os.path.join(BASE_DIR, 'logs', 'django.log'),
            'formatter': 'verbose',
        },
        'mail_admins': {
            'level': 'ERROR',
            'class': 'django.utils.log.AdminEmailHandler',
            'formatter': 'verbose',
        }
    },
    'root': {
        'handlers': ['console'],
    },
    'loggers': {
        'django': {
            'handlers': ['console', 'file'],
            'level': 'INFO',
        },
        'django.request': {
            'handlers': ['mail_admins'],
            'level': 'ERROR',
            'propagate': True,
        },
        'myapp': {
            'handlers': ['console', 'file'],
            'level': 'DEBUG',
            'propagate': True,
        },
    }
}

Using Logging

# views.py
import logging

# Get logger
logger = logging.getLogger(__name__)

def my_view(request):
    logger.debug('This is debug information')
    logger.info('This is info log')
    logger.warning('This is warning')
    logger.error('This is error')
    logger.critical('This is critical error')
    
    try:
        # Some code that might fail
        result = risky_operation()
    except Exception as e:
        logger.exception('Operation failed: %s', e)
        raise
    
    return render(request, 'template.html')

# Using structured logging
logger.info('User logged in', extra={
    'user_id': request.user.id,
    'ip_address': request.META.get('REMOTE_ADDR'),
    'user_agent': request.META.get('HTTP_USER_AGENT'),
})

Cache System

Cache Configuration

# settings.py

# Memory cache (development environment)
CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
        'LOCATION': 'unique-snowflake',
    }
}

# Redis cache (recommended for production)
CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.redis.RedisCache',
        'LOCATION': 'redis://127.0.0.1:6379/1',
        'OPTIONS': {
            'CLIENT_CLASS': 'django_redis.client.DefaultClient',
        }
    }
}

# Memcached cache
CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache',
        'LOCATION': '127.0.0.1:11211',
    }
}

# Multi-level cache
CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.redis.RedisCache',
        'LOCATION': 'redis://127.0.0.1:6379/1',
    },
    'sessions': {
        'BACKEND': 'django.core.cache.backends.redis.RedisCache',
        'LOCATION': 'redis://127.0.0.1:6379/2',
    }
}

# Session cache
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
SESSION_CACHE_ALIAS = 'sessions'

Using Cache

# views.py
from django.core.cache import cache
from django.views.decorators.cache import cache_page
from django.utils.decorators import method_decorator

# Manual caching
def expensive_view(request):
    # Try to get from cache
    result = cache.get('expensive_calculation')
    
    if result is None:
        # Cache miss, perform calculation
        result = perform_expensive_calculation()
        
        # Store in cache, expire in 300 seconds
        cache.set('expensive_calculation', result, 300)
    
    return render(request, 'result.html', {'result': result})

# View cache decorator
@cache_page(60 * 15)  # Cache for 15 minutes
def cached_view(request):
    return render(request, 'template.html')

# Class-based view cache
@method_decorator(cache_page(60 * 15), name='dispatch')
class CachedView(View):
    def get(self, request):
        return render(request, 'template.html')

# Template fragment cache
# template.html
{% load cache %}
{% cache 500 sidebar request.user.username %}
    <!-- Complex sidebar content -->
{% endcache %}

# Low-level cache API
from django.core.cache import caches

# Use specific cache
cache = caches['sessions']
cache.set('key', 'value', 300)
value = cache.get('key')

# Batch operations
cache.set_many({'a': 1, 'b': 2, 'c': 3}, 300)
values = cache.get_many(['a', 'b', 'c'])

# Atomic operations
cache.add('key', 'value', 300)  # Only set if key doesn't exist
cache.delete('key')
cache.clear()  # Clear all cache

Chapter Summary

This chapter covered Django's fundamental concepts and core systems in depth:

Key Points:

  • MVT Architecture: Model-View-Template design pattern
  • Settings System: Project configuration and environment management
  • Application Configuration: AppConfig class and application lifecycle
  • Middleware System: Request/response processing hooks
  • Signal System: Decoupled event notification mechanism
  • Logging System: Structured logging
  • Cache System: Performance optimization tool

Important Concepts:

  • Django's request-response flow
  • Middleware execution order and purpose
  • Signal sending and receiving mechanism
  • Cache strategies and usage scenarios

Best Practices:

  • Organize settings files properly
  • Use middleware order correctly
  • Use signals appropriately for business logic
  • Configure structured logging
  • Choose appropriate cache strategies

In the next chapter, we will learn about Django views and URL configuration to understand how to handle HTTP requests and responses.

Further Reading