Project Structure Deep Dive

Running startproject generated a handful of files without much explanation of what each one is for. Before adding TaskFlow's own code, it's worth slowing down and going through every generated file, especially settings.py, whose variables you'll be coming back to again and again for the rest of this track.

The Generated Layout

After running django-admin startproject taskflow_project ., your folder looks like this:

taskflow/
├── manage.py
└── taskflow_project/
    ├── __init__.py
    ├── settings.py
    ├── urls.py
    ├── asgi.py
    └── wsgi.py
File / FolderPurpose
manage.pyThe command-line entry point for this specific project — runs the server, migrations, and every other management command
taskflow_project/The project's configuration package — not an app, just settings and wiring
taskflow_project/__init__.pyAn empty file that marks the folder as an importable Python package
taskflow_project/settings.pyEvery project-wide setting: installed apps, database connection, templates, static files, and more
taskflow_project/urls.pyThe project's root URL configuration — the starting point for routing every incoming request
taskflow_project/wsgi.pyThe entry point traditional (synchronous) production servers use to run TaskFlow
taskflow_project/asgi.pyThe entry point asynchronous-capable production servers use to run TaskFlow

Touring settings.py: The Variables You'll Use Constantly

settings.py is a plain Python file — every "setting" is just a module-level variable that Django reads at startup. A handful of these matter enough to understand right away:

import os
from pathlib import Path

BASE_DIR = Path(__file__).resolve().parent.parent

DEBUG = True

ALLOWED_HOSTS = []

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}

STATIC_URL = 'static/'
  • DEBUG — when True, Django shows detailed error pages with full tracebacks and configuration details whenever something goes wrong. Extremely useful while developing TaskFlow locally, and extremely dangerous if left on for a live site (more on this below).
  • ALLOWED_HOSTS — a list of hostnames/domains this Django project is allowed to serve. It's mostly ignored while DEBUG is True, but Django enforces it strictly once DEBUG is False, as a defense against certain HTTP header attacks.
  • INSTALLED_APPS — the list of every app Django should load for this project, including its own built-in apps (admin, auth, sessions) and, soon, TaskFlow's own taskflow app. We'll add it here in the next lesson.
  • DATABASES — the connection details for TaskFlow's database. By default this points at a local SQLite file, which is perfect for learning and small projects; we'll work with this in depth in Models & Migrations.
  • TEMPLATES — configuration for how Django finds and renders the HTML template files that make up TaskFlow's pages, covered fully in Django Template Language.
  • STATIC_URL — the URL prefix Django uses to serve CSS, JavaScript, and image files, expanded on in Static Files.

ROOT_URLCONF and the Project's urls.py

Another setting worth knowing about early is ROOT_URLCONF, which by default looks like this:

ROOT_URLCONF = 'taskflow_project.urls'

This single line tells Django exactly where to start looking when a request comes in: at the urlpatterns list inside taskflow_project/urls.py. Every URL TaskFlow responds to — the task list, a project's detail page, the admin site — is ultimately reachable because it's connected, directly or indirectly, from this one root file. We'll dig into exactly how that works in URL Routing, a couple of lessons from now.

wsgi.py and asgi.py: Talking to Production Servers

wsgi.py and asgi.py aren't files you'll edit often, but they matter once TaskFlow needs to run somewhere other than your own machine. Both files expose a small "application" object that a production web server (like Gunicorn or uWSGI for WSGI, or Daphne or Uvicorn for ASGI) uses to actually talk to Django and hand it incoming requests. The development server we've been using (python manage.py runserver) is a stand-in for this during local development; a real production deployment swaps it out for one of these dedicated servers, a process we'll cover fully in Deployment.

Why DEBUG = True Must Never Reach Production

It's worth calling this out clearly and early, because it's a genuine, well-documented security risk rather than a stylistic preference. When DEBUG is True and an unhandled error occurs, Django's error page shows a full Python traceback, the values of local variables at the point of failure, your installed apps, and other internal configuration details — all visible to whoever triggered the error. On a local development machine that's a helpful debugging tool. On a live, public site, it can hand an attacker a detailed map of your application's internals.

Note: This isn't the last time this comes up — we'll revisit DEBUG specifically as part of CSRF & Security and again when preparing TaskFlow for a real server in Deployment. The safe pattern, previewed here and covered in full in Environment Configuration, is to load DEBUG from an environment variable that defaults to False, rather than hardcoding True directly in settings.py.
Next up: With the project's structure clear, head to Your First App to create TaskFlow's actual feature module and register it in INSTALLED_APPS.