Django Project Structure
Learn the Django project structure: manage.py, settings, urls, apps. Understand how files work together with a hands-on exercise and what to study next.
Focus: Django project structure
You've just run django-admin startproject mysite and you're staring at a tree of files. It's tempting to think of each one as a separate piece of configuration, but that's the wrong mental model — and it leads to hours of confusion when you can't figure out where to put what. The Django project structure isn't random bureaucracy; it's a deliberate split between project-level (the site's configuration and entry points) and app-level (the features themselves). Understanding this split isn't about memorizing file names — it's about knowing where to write your code so Django can find it, a skill you'll use in every single lesson that follows in this track.
The problem this lesson solves
When you start a new Django project, the generated files can feel like a random pile of Python modules. You might wonder: Why is there a settings.py and a urls.py? What does manage.py do that's so special? And why do I need another folder called an 'app'? The confusion is real because those files work as a system — a request travels through them in a fixed order, and each file has a single job. If you don't know that system, you'll put your views in the wrong file, your URLs in the wrong place, or your settings in settings.py and then be surprised when nothing works. This lesson gives you the map, so you can navigate any Django codebase with confidence — not just the one you generate today.
Core concept / mental model
Think of a Django project as a theater production. The project is the theater itself: the building, the stage, the lighting rig, the box office. It's the container and the configuration. The apps are the actual plays performed on that stage. One theater can host many plays, and each play has its own cast, script, and props — but they all use the same stage, lights, and sound system.
In Django terms:
- The project is a Python package that holds global configuration (
settings.py), the root URLconf (urls.py), and entry points (manage.py,wsgi.py,asgi.py). It's the infrastructure. - An app is a self-contained module that implements a specific feature — e.g.,
blog,polls,users. It bundles its own models, views, URLs, templates, and tests. It's the feature.
A key mental shift: you never write your main business logic in the project files. You write it inside an app, and then you point the project at the app by including it in INSTALLED_APPS and hooking its URLs into the root urls.py.
Here's the request flow — the invisible thread that links all the files:
- A browser sends a request to your server (e.g.,
http://yoursite.com/blog/). - The server hands it to
wsgi.pyorasgi.py, which tells Django about the environment. - Django reads
settings.pyto load all configuration. - Django looks at
urls.py(the root URLconf) and matches the URL path. - The matched entry calls a view — which lives inside an app — and that view uses models and templates to build a response.
That's the whole dance. Every file in the project structure is there to support one of those steps.
How it works step by step
Let's break down the key files in a freshly generated project. Run django-admin startproject mysite and you get a folder named mysite/ with a nested folder also named mysite/ (this is normal, not a mistake) plus a manage.py.
The inner folder is the project package, and it contains:
settings.py— the global config: database, installed apps, middleware, templates, static files, secret key, timezone, and more. This is the brain.urls.py— the root URLconf; a list of URL patterns that map URLs to views. It's the map.wsgi.py/asgi.py— entry points for web servers (WSGI for traditional, ASGI for async). You rarely touch these.__init__.py— marks the folder as a Python package.
Outside, manage.py is a thin wrapper around django-admin that sets the default settings module and lets you run commands like runserver, makemigrations, and migrate from the command line.
Step-by-step walkthrough
- Start a project —
django-admin startproject mysitecreates the project skeleton. - Inspect the files — open
mysite/settings.py; you'll seeINSTALLED_APPSwith default apps likedjango.contrib.admin,auth,contenttypes, etc. - Create an app — inside the project folder, run
python manage.py startapp blogto generate ablog/folder. - Register the app — add
'blog'toINSTALLED_APPSinsettings.py. - Add a URL — import your app's
viewsin the rooturls.pyand add a path, e.g.,path('blog/', include('blog.urls')). - Write code — in the app, define views, models, templates, and a
urls.pyfor the app's own URL routes. - Run the server —
python manage.py runserverand visithttp://127.0.0.1:8000/blog/.
Binding an app to the project is often called “wiring it up.” Until you do steps 4 and 5, Django won't know your app exists, and your URLs won't work.
Hands-on walkthrough
Time to get your hands dirty. We'll create a bare-bones project and an app, then wire the app in.
Step 1: Create the project
Assuming Python 3.10+ and Django installed, from your terminal:
mkdir django-structure-demo && cd django-structure-demo
# Create a virtual environment and activate it (if you haven't already)
python -m venv venv
source venv/bin/activate # on Windows: venv\Scripts\activate
pip install django
# Start the project
django-admin startproject mysite
This generates the structure we'll now explore.
Step 2: Examine the tree
Use tree (or find on macOS/Linux) to see the files:
cd mysite
tree .
Expected output (simplified):
.
├── manage.py
└── mysite
├── __init__.py
├── asgi.py
├── settings.py
├── urls.py
└── wsgi.py
Note how the outer mysite folder is your project root, and the inner mysite is the actual Python package with the configuration files.
Step 3: Create and wire an app
Now create a blog app and wire it into the project:
python manage.py startapp blog
Add 'blog' to INSTALLED_APPS in mysite/settings.py:
# mysite/settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'blog', # our new app
]
Create a minimal view in blog/views.py:
# blog/views.py
from django.http import HttpResponse
def index(request):
return HttpResponse("Hello from the blog app!")
Create blog/urls.py and define a single URL route:
# blog/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='index'),
]
Now hook the app's URLs into the project root urls.py:
# mysite/urls.py
from django.contrib import admin
from django.urls import include, path
urlpatterns = [
path('admin/', admin.site.urls),
path('blog/', include('blog.urls')),
]
Run the development server:
python manage.py runserver
Visit http://127.0.0.1:8000/blog/ and you should see “Hello from the blog app!” — your app is wired into the project.
What just happened?
The request for /blog/ hit the root URLconf (mysite/urls.py), which matched 'blog/' and handed off to blog/urls.py via include(). That file matched the empty path and called blog.views.index. Each file played its designated role — that's the project structure in action.
Compare options / when to choose what
You might wonder how much to put in a single project vs. multiple apps, or whether to use Django's built-in admin vs. custom views. Here's a practical comparison:
| Aspect | Single app per project | Multiple apps per project |
|---|---|---|
| Use case | Simple site or prototype — one feature | Real-world app with distinct domains (auth, blog, shop) |
| Pros | Less cross-file jumping; faster start | Clear separation of concerns; easier to reuse and scale |
| Cons | Grows messy as features multiply | Slightly more files to navigate at first |
| Best for | Learning, demos, MVP | Production Django projects |
Similarly, you'll often see choices about where to put urls.py:
- Project-level
urls.py— holds admin URLs and top-level includes. Good for global routes. - App-level
urls.py— each app owns its own routes. This is the recommended pattern for maintainability; you keep app routes inside the app.
Pro tip: Follow Django's app arcitecture — each app should be self-contained. If you find yourself copying a view from one app to another, consider extracting it into a shared app or a mixin, not into the project files.
Troubleshooting & edge cases
Common error: ModuleNotFoundError: No module named 'blog'
You tried to import blog in urls.py but didn't add it to INSTALLED_APPS. Fix: add the app to INSTALLED_APPS first, then restart the server.
Common error: URL pattern doesn't match
You added path('blog/', include('blog.urls')), but the app's urls.py starts with path('', ...) and you try to visit /blog (without trailing slash). Django will redirect to /blog/ only if APPEND_SLASH is True (the default). If you see a 404, double-check the trailing slash and the exact URL path.
Edge case: Two apps with the same name for a URL
If you define a URL pattern with name='index' in both blog and polls, Django will use the one that appears last in INSTALLED_APPS. To avoid ambiguity, use namespacing with the include argument:
path('blog/', include(('blog.urls', 'blog'), namespace='blog'))
Edge case: Forgetting (request) parameter in a view
If your view signature is def index() instead of def index(request), Django raises TypeError: index() takes no arguments (1 given). Always include request as the first parameter.
Common mistake: Editing wsgi.py or asgi.py unnecessarily
These are entry points; you rarely need to change them. If you edit them and break them, the server won't start. Leave them alone unless you know what you're doing.
What you learned & what's next
You now understand the Django project structure as a two-layer system: the project provides configuration and entry points, and apps contain the actual business logic. You know the purpose of manage.py, settings.py, urls.py, and app files, and you've wired your first app into a project. Specifically, you can explain the core idea behind the project/app split, and you've completed a practical exercise where you created an app, registered it, and connected its URLs — both core objectives of this lesson.
This foundation sets you up for the next step in the track: Django apps and URL routing — where you'll dive deeper into app creation, views.py and urls.py in more detail. But you can already see how the structure makes that work. Keep this mental model as you move forward; every Django project you touch will follow the same pattern.
Practice recap
Create a second app called polls in the same project, define a simple view, and wire it in. Then try moving the index view from blog to polls by updating URLs — this forces you to understand how app boundaries and URL includes work together.
Common mistakes
- Placing business logic in the project package (e.g., in
settings.pyorurls.py) instead of inside an app — always create an app for features. - Forgetting to add the new app to
INSTALLED_APPSinsettings.py— Django won't see your models, URLs, or views until you do. - Editing
wsgi.py/asgi.pywithout knowing what they do — these entry points are rarely modified, and breaking them prevents the server from starting. - Using URL names that conflict between apps without namespacing, leading to the wrong view being called.
Variations
- Project-level
urls.pycan contain all URL patterns directly rather than usinginclude()for small projects — acceptable for tiny prototypes but less maintainable. - Using Django REST Framework's
DefaultRouterto auto-generate URL patterns for API views, instead of manually listing each endpoint. - Organizing reusable functionality as a shared app (e.g.,
common) vs. duplicating code across apps — a design choice for DRY code.
Real-world use cases
- Building an e-commerce site with separate
products,cart, andusersapps, each owning its own models and URLs, wired into a single project. - Maintaining a company intranet with one project and multiple reusable apps for HR, IT, and finance, so teams can work on apps independently.
- Deploying a Django project to a platform like Heroku or AWS, where
wsgi.pyis the entry point for the server — understanding it helps with deployment configuration.
Key takeaways
- Django splits concerns into project (config/entry points) vs. apps (business logic) — never the twain shall mix.
manage.pyis your command-line tool; it wrapsdjango-adminand applies project settings.- The request flow is: server →
wsgi.py/asgi.py→settings.py→ rooturls.py→ app view. - Every app must be registered in
INSTALLED_APPSand its URLs included in the root URLconf to work. - App-level
urls.pyis the recommended pattern; use namespacing to avoid URL name collisions.
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.