Django Project Structure
Introduction
When you create a new Django project, it sets up a specific directory structure with various files and folders. Understanding this structure is crucial for effective Django development as it follows the Model-View-Controller (MVC) pattern (though Django calls it Model-View-Template or MVT). This organized approach helps you maintain clean, reusable, and maintainable code.
In this tutorial, we'll explore the standard Django project structure, explain what each component does, and share best practices for organizing your Django applications.
Basic Project Structure
Let's start by creating a new Django project to see the default structure it generates. You'll need Django installed in your environment first:
# Install Django if you haven't already
pip install django
# Create a new project
django-admin startproject myproject
After running this command, Django creates a directory structure that looks like this:
myproject/
├── manage.py
└── myproject/
├── __init__.py
├── asgi.py
├── settings.py
├── urls.py
└── wsgi.py
Understanding Each Component
Let's examine each file and understand its purpose:
manage.py
This is a command-line utility that allows you to interact with your Django project. You'll use it to run the server, create migrations, apply migrations, and more.
# Run development server
python manage.py runserver
# Create migrations
python manage.py makemigrations
# Apply migrations
python manage.py migrate
myproject/
(Inner Directory)
This is the actual Python package for your project. Its name is what you'll use to import modules from it.
myproject/__init__.py
An empty file that tells Python this directory should be considered a Python package.
myproject/settings.py
This file contains all the configuration for your Django project. Here's where you specify:
- Database configuration
- Installed applications
- Middleware
- Static files settings
- And much more
# Example settings.py snippet
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# Your custom apps go here
]
# Database configuration
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
# Static files settings
STATIC_URL = '/static/'
myproject/urls.py
This file contains URL declarations for your project - essentially your site's "table of contents."
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
# Add your app URLs here
# path('myapp/', include('myapp.urls')),
]
myproject/asgi.py
An entry-point for ASGI-compatible web servers to serve your project. ASGI (Asynchronous Server Gateway Interface) allows for asynchronous Django applications.
myproject/wsgi.py
An entry-point for WSGI-compatible web servers to serve your project. WSGI (Web Server Gateway Interface) is what most production deployments use.
Adding Applications
Django follows a "project and apps" structure. A project contains multiple apps, and each app serves a specific purpose in your project.
Let's add an app to our project:
python manage.py startapp blog
This creates a new directory structure:
myproject/
├── blog/
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── migrations/
│ │ └── __init__.py
│ ├── models.py
│ ├── tests.py
│ └── views.py
├── manage.py
└── myproject/
├── __init__.py
├── asgi.py
├── settings.py
├── urls.py
└── wsgi.py
Understanding App Components
Each app directory contains these key files:
admin.py
Where you register your models with the Django admin site.
from django.contrib import admin
from .models import Post
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
list_display = ('title', 'author', 'created_date', 'published_date')
list_filter = ('published_date', 'author')
search_fields = ('title', 'content')
apps.py
Contains configuration specific to this app.
from django.apps import AppConfig
class BlogConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'blog'
migrations/
Directory containing database migrations for this app.
models.py
Where you define your database models using Django's ORM.
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
class Post(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
created_date = models.DateTimeField(default=timezone.now)
published_date = models.DateTimeField(blank=True, null=True)
author = models.ForeignKey(User, on_delete=models.CASCADE)
def publish(self):
self.published_date = timezone.now()
self.save()
def __str__(self):
return self.title
tests.py
Where you write tests for your app.
from django.test import TestCase
from django.contrib.auth.models import User
from .models import Post
class PostModelTests(TestCase):
def setUp(self):
self.user = User.objects.create_user(username='testuser', password='password')
def test_create_post(self):
post = Post(title="Test Post", content="Test Content", author=self.user)
post.save()
self.assertEqual(post.title, "Test Post")
self.assertEqual(post.content, "Test Content")
self.assertEqual(post.author, self.user)
views.py
Contains the view functions or classes that process HTTP requests and return responses.
from django.shortcuts import render, get_object_or_404
from .models import Post
def post_list(request):
posts = Post.objects.filter(published_date__isnull=False).order_by('-published_date')
return render(request, 'blog/post_list.html', {'posts': posts})
def post_detail(request, pk):
post = get_object_or_404(Post, pk=pk)
return render(request, 'blog/post_detail.html', {'post': post})
Enhanced Project Structure for Larger Applications
As your project grows, the basic structure might become inadequate. Here's a more advanced structure recommended for larger projects:
myproject/
├── apps/
│ ├── blog/
│ │ ├── __init__.py
│ │ ├── admin.py
│ │ ├── apps.py
│ │ ├── forms.py
│ │ ├── managers.py
│ │ ├── migrations/
│ │ ├── models.py
│ │ ├── services.py
│ │ ├── tests/
│ │ │ ├── __init__.py
│ │ │ ├── test_forms.py
│ │ │ ├── test_models.py
│ │ │ └── test_views.py
│ │ ├── urls.py
│ │ └── views.py
│ └── users/
│ ├── ...similar structure as blog
├── config/
│ ├── __init__.py
│ ├── asgi.py
│ ├── settings/
│ │ ├── __init__.py
│ │ ├── base.py
│ │ ├── dev.py
│ │ └── prod.py
│ ├── urls.py
│ └── wsgi.py
├── media/
├── static/
│ ├── css/
│ ├── js/
│ └── images/
├── templates/
│ ├── base.html
│ ├── blog/
│ │ ├── post_detail.html
│ │ └── post_list.html
│ └── users/
│ ├── login.html
│ └── profile.html
├── manage.py
├── requirements/
│ ├── base.txt
│ ├── dev.txt
│ └── prod.txt
└── .env
Key Enhancements
-
Separate apps directory: Keeps all your applications in one place.
-
Config directory: Replaces the inner project directory and better communicates its purpose.
-
Split settings: Divides settings into base, development, and production files.
python# base.py - Settings common to all environments
DEBUG = False
# ... other common settings
# dev.py
from .base import *
DEBUG = True
# ... other development-specific settings
# prod.py
from .base import *
DEBUG = False
# ... other production-specific settings -
Dedicated templates directory: Keeps all templates in one place, organized by app.
-
Static and media directories: Clear organization for static files and user-uploaded content.
-
Requirements split: Separate requirement files for different environments.
Best Practices for Django Project Structure
-
Follow Django's app-based architecture: Each app should do one thing and do it well.
-
Keep apps small and focused: If an app grows too large, consider splitting it.
-
Use meaningful app names: Names should clearly indicate the app's purpose.
-
Create reusable apps: Design apps to be reusable across projects when possible.
-
Organize templates and static files clearly: Use namespacing to avoid conflicts.
-
Split settings by environment: Use different settings for development, testing, and production.
-
Use environment variables for sensitive data: Never commit sensitive information like API keys or passwords.
-
Follow URL namespacing conventions: Use app name as URL namespace.
Real-World Example: Blog Application
Let's look at a more detailed example of our blog app structure with all the files we might need:
blog/
├── __init__.py
├── admin.py
├── apps.py
├── forms.py
├── managers.py
├── migrations/
├── models.py
├── services.py
├── templates/
│ └── blog/
│ ├── base.html
│ ├── post_confirm_delete.html
│ ├── post_detail.html
│ ├── post_form.html
│ └── post_list.html
├── tests/
│ ├── __init__.py
│ ├── test_forms.py
│ ├── test_models.py
│ └── test_views.py
├── urls.py
└── views.py
With these files:
forms.py
:
from django import forms
from .models import Post
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ('title', 'content')
urls.py
:
from django.urls import path
from . import views
app_name = 'blog'
urlpatterns = [
path('', views.post_list, name='post_list'),
path('post/<int:pk>/', views.post_detail, name='post_detail'),
path('post/new/', views.post_new, name='post_new'),
path('post/<int:pk>/edit/', views.post_edit, name='post_edit'),
path('post/<int:pk>/delete/', views.post_delete, name='post_delete'),
]
services.py
(for business logic):
from django.utils import timezone
from .models import Post
def publish_post(post_id):
"""Service to publish a post"""
post = Post.objects.get(id=post_id)
post.published_date = timezone.now()
post.save()
return post
managers.py
(for custom model managers):
from django.db import models
class PublishedPostManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(published_date__isnull=False)
Updating models.py
to use the custom manager:
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
from .managers import PublishedPostManager
class Post(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
created_date = models.DateTimeField(default=timezone.now)
published_date = models.DateTimeField(blank=True, null=True)
author = models.ForeignKey(User, on_delete=models.CASCADE)
# Managers
objects = models.Manager() # Default manager
published = PublishedPostManager() # Custom manager
def publish(self):
self.published_date = timezone.now()
self.save()
def __str__(self):
return self.title
Summary
Django's project structure is designed to promote clean, maintainable, and scalable code. By understanding each component's purpose and following best practices, you'll build applications that are easier to develop, test, and maintain.
Remember these key points:
- Django follows a "project and apps" structure
- Each app should have a single responsibility
- As projects grow, organize them with separate directories for static files, media, templates, etc.
- Split settings by environment
- Keep sensitive data in environment variables
- Test your code thoroughly
With this solid foundation in Django's project structure, you'll be well-prepared to build robust web applications.
Additional Resources
- Official Django Documentation on Applications
- Two Scoops of Django - A popular book on Django best practices
- Django Project Optimization Guide
Exercises
- Create a new Django project and add two apps: a blog app and a users app.
- Restructure an existing Django project to follow the enhanced project structure described in this tutorial.
- Create a custom settings structure with base, development, and production settings.
- Set up a project that uses environment variables for database credentials and secret key.
- Create an app with models, views, forms, and tests following the structure outlined in this tutorial.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)