Setting Up Your First Django Project Like a Pro

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.

Author

  • Mohammad Golam Dostogir, Software Engineer specializing in Python, Django, and AI solutions. Active contributor to open-source projects and tech communities, with experience delivering applications for global companies.
    GitHub

    View all posts
Index