When I first started learning Django two years ago, I made every mistake in the book. Messy project structure, hardcoded settings, and zero organization. After building several production apps, I’ve learned that how you start your Django project determines whether you’ll love or hate maintaining it six months later.
Today I’ll walk you through setting up a Django project the right way from day one. We’ll build a bookstore application that you can actually use as a template for future projects.
Why Project Structure Matters More Than You Think
I’ve seen too many Django projects that started simple and became unmaintainable nightmares. The secret isn’t just following Django conventions – it’s thinking about your future self who will need to debug this code at 2 AM.
Getting Started the Right Way
First, let’s create a clean development environment. I always use virtual environments because dependency conflicts are the worst way to spend your weekend.
# Create a virtual environment for our project
python -m venv django_bookstore
source django_bookstore/bin/activate # On Windows: django_bookstore\\Scripts\\activate
# Install Django (I'm using 4.0 since it's the latest stable)
pip install django==4.0
# Create our main project
django-admin startproject bookstore_project
cd bookstore_project
# Create our first app
python manage.py startapp inventory
Organizing Settings Like a Professional
Here’s where most tutorials go wrong. They show you the default settings.py and call it a day. In real projects, you need different settings for development, staging, and production.
# bookstore_project/settings.py
import os
from pathlib import Path
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = os.environ.get('SECRET_KEY', 'your-dev-secret-key-here')
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = os.environ.get('DEBUG', 'True') == 'True'
ALLOWED_HOSTS = ['localhost', '127.0.0.1']
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# Our apps
'inventory.apps.InventoryConfig',
]
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',
]
ROOT_URLCONF = 'bookstore_project.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR / 'templates'],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
# Database
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
# Static files (CSS, JavaScript, Images)
STATIC_URL = '/static/'
STATICFILES_DIRS = [BASE_DIR / 'static']
STATIC_ROOT = BASE_DIR / 'staticfiles'
# Media files
MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR / 'media'
# Default primary key field type
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
Environment Variables for Sane Configuration
Never hardcode sensitive information. Create a .env
file for local development:
# .env (add this to your .gitignore!)
SECRET_KEY=your-super-secret-key-that-nobody-should-see
DEBUG=True
DATABASE_URL=sqlite:///db.sqlite3
Install python-decouple to handle environment variables cleanly:
pip install python-decouple
# Updated settings.py
from decouple import config
SECRET_KEY = config('SECRET_KEY')
DEBUG = config('DEBUG', default=False, cast=bool)
Directory Structure That Actually Makes Sense
After working on teams with 5+ developers, I’ve learned that a good directory structure is like a good API – it should be obvious how to use it.
bookstore_project/
├── bookstore_project/
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── inventory/
│ ├── migrations/
│ ├── templates/
│ │ └── inventory/
│ ├── static/
│ │ └── inventory/
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── models.py
│ ├── tests.py
│ ├── urls.py
│ └── views.py
├── templates/
├── static/
├── media/
├── requirements.txt
├── .env
├── .gitignore
└── manage.py
Your First App Configuration
Most people skip the apps.py configuration, but it’s where you define how your app behaves within the larger project.
# inventory/apps.py
from django.apps import AppConfig
class InventoryConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'inventory'
verbose_name = 'Book Inventory Management'
def ready(self):
# Import signals here if you have any
pass
Setting Up URL Routing
URL configuration is like the roads in your application. Make them clear and logical from the start.
# bookstore_project/urls.py
from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('inventory.urls')),
]
# Serve media files in development
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
# inventory/urls.py
from django.urls import path
from . import views
app_name = 'inventory'
urlpatterns = [
path('', views.home, name='home'),
]
Quick Test to Make Sure Everything Works
Let’s create a simple view to test our setup:
# inventory/views.py
from django.shortcuts import render
from django.http import HttpResponse
def home(request):
return HttpResponse("Welcome to our bookstore! Everything is working perfectly.")
Now run your development server:
python manage.py runserver
Visit http://127.0.0.1:8000
and you should see your welcome message.
Essential Development Tools
Before we move on, let’s set up some tools that will save you hours of debugging later:
pip install django-debug-toolbar
pip freeze > requirements.txt
The django-debug-toolbar is incredibly useful for understanding what your application is doing behind the scenes.
What’s Next
We’ve built a solid foundation that you can use for any Django project. The key principles we covered – environment separation, clean structure, and proper configuration – will serve you well as your applications grow.
In my next article, we’ll dive into Django models and create the data structure for our bookstore. We’ll cover relationships, custom methods, and some advanced techniques I’ve picked up from building e-commerce platforms.
The project structure we set up today might seem like overkill for a simple tutorial, but trust me – you’ll thank yourself later when you’re building real applications that need to scale.