
Django Project Structure Best Practices
Whether you're just starting with Django or you've been using it for a while, getting your project structure right is crucial. A well-organized project makes your code more maintainable, scalable, and easier for other developers to understand. Let's dive into the best practices that will help you structure your Django projects like a pro.
The Standard Django Project Layout
When you create a new Django project using django-admin startproject myproject
, you get a basic structure that looks like this:
myproject/
manage.py
myproject/
__init__.py
settings.py
urls.py
asgi.py
wsgi.py
This is a good starting point, but for any non-trivial application, you'll want to expand on this structure. The key is to separate your project configuration from your actual applications.
Best practice: Think of your project as a container for apps, and each app should handle a specific piece of functionality.
Organizing Your Applications
Django encourages you to build your project as a collection of reusable apps. Each app should focus on doing one thing well. Here's how I like to structure my projects:
myproject/
manage.py
config/
__init__.py
settings/
__init__.py
base.py
development.py
production.py
testing.py
urls.py
wsgi.py
asgi.py
apps/
__init__.py
core/
__init__.py
models.py
views.py
admin.py
apps.py
tests/
__init__.py
test_models.py
test_views.py
migrations/
__init__.py
users/
__init__.py
models.py
views.py
# ... other app files
static/
css/
js/
images/
templates/
base.html
core/
index.html
users/
login.html
profile.html
media/
requirements/
base.txt
development.txt
production.txt
docs/
.env
.gitignore
This structure keeps everything organized and makes it clear where to find specific components.
Settings Management
One of the most important improvements you can make is splitting your settings into multiple files. This allows you to have different configurations for development, testing, and production environments.
Create a settings directory and move your original settings.py into it, then create separate files:
# config/settings/base.py
import os
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent.parent.parent
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'apps.core',
'apps.users',
]
# ... rest of your base settings
# config/settings/development.py
from .base import *
DEBUG = True
SECRET_KEY = 'your-development-secret-key'
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
Then update your manage.py
and wsgi.py
to point to the appropriate settings:
# manage.py
#!/usr/bin/env python
import os
import sys
if __name__ == '__main__':
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings.development')
# ... rest of the file
This approach gives you much better control over your environment-specific configurations and keeps sensitive production settings separate.
Application Structure Within Apps
Each app within your project should also follow a consistent structure. Here's what a well-organized app looks like:
apps/users/
__init__.py
admin.py
apps.py
models.py
views.py
urls.py
forms.py
managers.py
signals.py
tests/
__init__.py
test_models.py
test_views.py
test_forms.py
migrations/
__init__.py
templates/
users/
login.html
profile.html
register.html
static/
users/
css/
js/
Notice how each app has its own templates and static files organized in subdirectories with the app name. This prevents naming conflicts and makes it clear which app a template belongs to.
File/Folder | Purpose | Importance |
---|---|---|
tests/ | Contains all test files | High - Ensures code quality |
templates/app_name/ | App-specific templates | High - Prevents naming conflicts |
static/app_name/ | App-specific static files | High - Organized asset management |
migrations/ | Database migration files | Critical - Never delete manually |
URL Configuration
Instead of putting all your URLs in the main urls.py
file, let each app manage its own URLs:
# config/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('apps.core.urls')),
path('users/', include('apps.users.urls')),
]
# apps/core/urls.py
from django.urls import path
from . import views
app_name = 'core'
urlpatterns = [
path('', views.home, name='home'),
path('about/', views.about, name='about'),
]
# apps/users/urls.py
from django.urls import path
from . import views
app_name = 'users'
urlpatterns = [
path('login/', views.login_view, name='login'),
path('logout/', views.logout_view, name='logout'),
path('profile/', views.profile, name='profile'),
]
This approach makes your URL configuration much more maintainable as your project grows.
Template Organization
When it comes to templates, I recommend using a base template that other templates extend:
<!-- 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 Project{% endblock %}</title>
{% block extra_head %}{% endblock %}
</head>
<body>
<header>
<!-- Navigation here -->
</header>
<main>
{% block content %}
{% endblock %}
</main>
<footer>
<!-- Footer content here -->
</footer>
{% block extra_js %}{% endblock %}
</body>
</html>
<!-- templates/core/index.html -->
{% extends "base.html" %}
{% block title %}Home - My Project{% endblock %}
{% block content %}
<h1>Welcome to our site!</h1>
<p>This is the home page content.</p>
{% endblock %}
Using template inheritance like this prevents code duplication and makes your templates much easier to maintain.
Static Files Management
For static files, Django's built-in system works well, but you need to organize them properly:
# In your settings
STATIC_URL = '/static/'
STATICFILES_DIRS = [
BASE_DIR / "static",
]
STATIC_ROOT = BASE_DIR / "staticfiles"
# For production, you'll also want:
MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR / "media"
Organize your static files by app to avoid conflicts:
static/
core/
css/
style.css
js/
main.js
images/
logo.png
users/
css/
user.css
js/
user.js
When you need to reference static files in templates, use the {% static %}
tag:
{% load static %}
<link rel="stylesheet" href="{% static 'core/css/style.css' %}">
<img src="{% static 'core/images/logo.png' %}" alt="Logo">
Environment Variables and Secrets
Never hardcode sensitive information like API keys, database passwords, or secret keys. Use environment variables:
# config/settings/base.py
import os
from django.core.exceptions import ImproperlyConfigured
def get_env_variable(var_name, default=None):
try:
return os.environ[var_name]
except KeyError:
if default is not None:
return default
error_msg = f"Set the {var_name} environment variable"
raise ImproperlyConfigured(error_msg)
SECRET_KEY = get_env_variable('DJANGO_SECRET_KEY')
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': get_env_variable('DB_NAME'),
'USER': get_env_variable('DB_USER'),
'PASSWORD': get_env_variable('DB_PASSWORD'),
'HOST': get_env_variable('DB_HOST', 'localhost'),
'PORT': get_env_variable('DB_PORT', '5432'),
}
}
Then create a .env
file (add it to .gitignore
!) for development:
DJANGO_SECRET_KEY=your-super-secret-key-here
DB_NAME=myproject_dev
DB_USER=myproject_user
DB_PASSWORD=secure_password
DB_HOST=localhost
DB_PORT=5432
DEBUG=True
Use python-decouple or django-environ packages to make this even easier.
Testing Structure
A good test structure is essential for maintaining code quality. Organize your tests to mirror your app structure:
apps/users/tests/
__init__.py
test_models.py
test_views.py
test_forms.py
test_managers.py
factories.py
Write tests for each component:
# apps/users/tests/test_models.py
from django.test import TestCase
from django.contrib.auth import get_user_model
from ..models import UserProfile
User = get_user_model()
class UserModelTest(TestCase):
def test_create_user(self):
user = User.objects.create_user(
username='testuser',
email='test@example.com',
password='testpass123'
)
self.assertEqual(user.username, 'testuser')
self.assertEqual(user.email, 'test@example.com')
self.assertTrue(user.check_password('testpass123'))
Regular testing saves you from countless headaches down the road and makes refactoring much safer.
Managing Requirements
Instead of a single requirements.txt file, use multiple files for different environments:
requirements/
base.txt
development.txt
production.txt
testing.txt
Your base.txt contains packages needed in all environments:
Django==4.2.7
psycopg2-binary==2.9.9
Pillow==10.0.1
development.txt includes base plus development tools:
-r base.txt
ipython==8.17.2
django-debug-toolbar==4.2.0
django-extensions==3.2.3
Then install with: pip install -r requirements/development.txt
Deployment Considerations
When structuring your project, keep deployment in mind. Here are some files you might need:
myproject/
...
Dockerfile
docker-compose.yml
.dockerignore
nginx/
nginx.conf
scripts/
deploy.sh
backup.sh
.github/
workflows/
ci.yml
Thinking about deployment from the beginning will save you from major restructuring later.
Common Pitfalls to Avoid
- Don't put all your code in one giant app - break it into logical components
- Avoid putting business logic in views - use services or model methods
- Don't hardcode settings - use environment variables
- Avoid putting all templates in one flat directory - use app-specific subdirectories
- Don't neglect tests - they're your safety net
Remember that the perfect structure doesn't exist - it depends on your specific project needs. Start with these best practices and adapt them as your project grows. The most important thing is consistency - whatever structure you choose, stick with it throughout your project.
What Django project structure tips have worked well for you? I'd love to hear about your experiences and any additional best practices you've discovered in your Django journey!