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.
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.
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.
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
manage.pyA command-line utility that lets you interact with your Django project. You'll use this to:
python manage.py runserverpython manage.py makemigrationspython manage.py migratepython manage.py createsuperuser# Example manage.py usage
python manage.py runserver 8000
python manage.py shell
python manage.py collectstatic
settings.pyContains 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.pyThe 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.pyEntry points for WSGI and ASGI-compatible web servers to serve your project.
models.pyDefines 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.pyContains 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.pyConfigures 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.pyApp 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
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/
Split settings for different environments:
settings/base.pyimport 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.pyfrom .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.pyfrom .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
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/
├── css/
│ ├── main.css
│ ├── components/
│ └── pages/
├── js/
│ ├── main.js
│ ├── components/
│ └── pages/
├── images/
│ ├── logos/
│ └── icons/
└── fonts/
# 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
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)
Use Django's path utilities:
# Bad
MEDIA_ROOT = '/var/www/media/'
# Good
MEDIA_ROOT = BASE_DIR / 'media'
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.
MVC vs MVT: Understanding Django's Architecture
Django implements the Model-View-Template (MVT) architectural pattern, which is a variation of the traditional Model-View-Controller (MVC) pattern. Understanding the differences and similarities between these patterns is crucial for effective Django development.
Installing Django
Setting up Django properly is crucial for a smooth development experience. This guide covers multiple installation methods, environment setup, and best practices for professional Django development.