Skip to content

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

python
# 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

python
# 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

python
# 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

python
# 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

python
# 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

python
# 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

python
# 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.

python
# 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

python
# 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

python
# 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

python
# 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.

python
# 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

python
# 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

python
# 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

python
# 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

python
# 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

python
# 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

python
# 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

Content is for learning and research only.