Introduction and Foundations

Project Structure Overview

Understanding Django's project structure is crucial for effective development. Django organizes code into projects and apps, each serving specific purposes in your web application architecture.

Project Structure Overview

Understanding Django's project structure is crucial for effective development. Django organizes code into projects and apps, each serving specific purposes in your web application architecture.

Django Project vs App

Project

A Django project is a collection of settings and apps for a particular website. A project can contain multiple apps, and an app can be in multiple projects.

App

A Django app is a web application that does something – e.g., a blog system, a database of public records, or a small poll app. Apps are designed to be portable and reusable.

Standard Django Project Structure

When you create a new Django project, you'll see this structure:

myproject/
├── manage.py
├── myproject/
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   ├── wsgi.py
│   └── asgi.py
└── myapp/
    ├── __init__.py
    ├── admin.py
    ├── apps.py
    ├── migrations/
    │   └── __init__.py
    ├── models.py
    ├── tests.py
    ├── urls.py
    └── views.py

Core Files and Directories

Project-Level Files

manage.py

A command-line utility that lets you interact with your Django project. You'll use this to:

  • Start the development server: python manage.py runserver
  • Create migrations: python manage.py makemigrations
  • Apply migrations: python manage.py migrate
  • Create superuser: python manage.py createsuperuser
# Example manage.py usage
python manage.py runserver 8000
python manage.py shell
python manage.py collectstatic

settings.py

Contains all configuration for your Django project. Key sections include:

# Database configuration
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'myproject_db',
        'USER': 'myuser',
        'PASSWORD': 'mypassword',
        'HOST': 'localhost',
        'PORT': '5432',
    }
}

# Installed apps
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'myapp',  # Your custom app
]

# Static files configuration
STATIC_URL = '/static/'
STATIC_ROOT = BASE_DIR / 'staticfiles'
STATICFILES_DIRS = [
    BASE_DIR / 'static',
]

urls.py

The URL dispatcher for your project. Maps URLs to views:

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('myapp.urls')),
    path('api/', include('api.urls')),
    path('blog/', include('blog.urls')),
]

wsgi.py and asgi.py

Entry points for WSGI and ASGI-compatible web servers to serve your project.

App-Level Files

models.py

Defines your data models (database schema):

from django.db import models
from django.contrib.auth.models import User

class Category(models.Model):
    name = models.CharField(max_length=100)
    slug = models.SlugField(unique=True)
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.name

class Post(models.Model):
    title = models.CharField(max_length=200)
    content = models.TextField()
    author = models.ForeignKey(User, on_delete=models.CASCADE)
    category = models.ForeignKey(Category, on_delete=models.CASCADE)
    published = models.BooleanField(default=False)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ['-created_at']

    def __str__(self):
        return self.title

views.py

Contains view functions or classes that handle requests:

from django.shortcuts import render, get_object_or_404
from django.http import JsonResponse
from django.views.generic import ListView
from .models import Post, Category

def post_list(request):
    posts = Post.objects.filter(published=True)
    return render(request, 'blog/post_list.html', {'posts': posts})

def post_detail(request, pk):
    post = get_object_or_404(Post, pk=pk, published=True)
    return render(request, 'blog/post_detail.html', {'post': post})

class CategoryListView(ListView):
    model = Category
    template_name = 'blog/category_list.html'
    context_object_name = 'categories'

admin.py

Configures the Django admin interface:

from django.contrib import admin
from .models import Post, Category

@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
    list_display = ['name', 'slug', 'created_at']
    prepopulated_fields = {'slug': ('name',)}

@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
    list_display = ['title', 'author', 'category', 'published', 'created_at']
    list_filter = ['published', 'category', 'created_at']
    search_fields = ['title', 'content']
    prepopulated_fields = {'slug': ('title',)}

apps.py

App configuration:

from django.apps import AppConfig

class BlogConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'blog'
    verbose_name = 'Blog Application'

    def ready(self):
        import blog.signals  # Import signals when app is ready

Advanced Project Structure

For larger projects, consider this enhanced structure:

myproject/
├── manage.py
├── requirements/
│   ├── base.txt
│   ├── development.txt
│   └── production.txt
├── config/
│   ├── __init__.py
│   ├── settings/
│   │   ├── __init__.py
│   │   ├── base.py
│   │   ├── development.py
│   │   └── production.py
│   ├── urls.py
│   ├── wsgi.py
│   └── asgi.py
├── apps/
│   ├── __init__.py
│   ├── accounts/
│   ├── blog/
│   ├── api/
│   └── core/
├── static/
│   ├── css/
│   ├── js/
│   └── images/
├── media/
├── templates/
│   ├── base.html
│   ├── accounts/
│   └── blog/
├── locale/
├── docs/
└── tests/

Environment-Specific Settings

Split settings for different environments:

settings/base.py

import os
from pathlib import Path

BASE_DIR = Path(__file__).resolve().parent.parent.parent

SECRET_KEY = os.environ.get('SECRET_KEY')

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    
    # Third-party apps
    'rest_framework',
    'corsheaders',
    
    # Local apps
    'apps.accounts',
    'apps.blog',
    'apps.api',
]

MIDDLEWARE = [
    'corsheaders.middleware.CorsMiddleware',
    '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',
]

ROOT_URLCONF = 'config.urls'

settings/development.py

from .base import *

DEBUG = True
ALLOWED_HOSTS = ['localhost', '127.0.0.1']

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

# Development-specific settings
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'

settings/production.py

from .base import *
import dj_database_url

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

# Database configuration from environment
DATABASES = {
    'default': dj_database_url.config(
        default=os.environ.get('DATABASE_URL')
    )
}

# Security settings
SECURE_SSL_REDIRECT = True
SECURE_HSTS_SECONDS = 31536000
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True

Templates Structure

Organize templates hierarchically:

templates/
├── base.html
├── includes/
│   ├── header.html
│   ├── footer.html
│   └── navigation.html
├── accounts/
│   ├── login.html
│   ├── register.html
│   └── profile.html
└── blog/
    ├── base.html
    ├── post_list.html
    ├── post_detail.html
    └── category_list.html

templates/base.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{% block title %}My Django Site{% endblock %}</title>
    {% load static %}
    <link rel="stylesheet" href="{% static 'css/main.css' %}">
    {% block extra_css %}{% endblock %}
</head>
<body>
    {% include 'includes/header.html' %}
    
    <main>
        {% block content %}{% endblock %}
    </main>
    
    {% include 'includes/footer.html' %}
    
    <script src="{% static 'js/main.js' %}"></script>
    {% block extra_js %}{% endblock %}
</body>
</html>

Static Files Organization

static/
├── css/
│   ├── main.css
│   ├── components/
│   └── pages/
├── js/
│   ├── main.js
│   ├── components/
│   └── pages/
├── images/
│   ├── logos/
│   └── icons/
└── fonts/

Best Practices

1. App Organization

  • Keep apps focused on a single responsibility
  • Use descriptive app names
  • Group related apps in subdirectories for large projects

2. Settings Management

  • Never commit sensitive data to version control
  • Use environment variables for configuration
  • Split settings by environment

3. File Naming Conventions

  • Use lowercase with underscores for Python files
  • Use descriptive names for templates and static files
  • Follow Django's naming conventions

4. Import Organization

# Standard library imports
import os
import sys

# Third-party imports
import requests
from django.shortcuts import render

# Local imports
from .models import Post
from .forms import PostForm

5. Documentation

  • Document complex business logic
  • Use docstrings for functions and classes
  • Maintain a README with setup instructions

Common Pitfalls to Avoid

1. Circular Imports

Avoid importing models between apps directly. Use string references instead:

# Bad
from otherapp.models import OtherModel

# Good
class MyModel(models.Model):
    other = models.ForeignKey('otherapp.OtherModel', on_delete=models.CASCADE)

2. Hardcoded Paths

Use Django's path utilities:

# Bad
MEDIA_ROOT = '/var/www/media/'

# Good
MEDIA_ROOT = BASE_DIR / 'media'

3. Settings in Version Control

Never commit sensitive settings:

# Bad - in settings.py
SECRET_KEY = 'your-secret-key-here'

# Good - use environment variables
SECRET_KEY = os.environ.get('SECRET_KEY')

This structure provides a solid foundation for Django projects of any size, from simple prototypes to complex enterprise applications.