Project Structure Best Practices

Recommended layout for apps, templates, static, media, settings packages, and shared utilities. Naming, import paths, and how to keep the project maintainable as it grows.

1. Introduction

A clean project structure makes your codebase easier to navigate, maintain, and scale. Django does not enforce a strict layout beyond the basics — which means developers often end up with messy structures as projects grow.

This guide shows you a practical, widely used layout for Django projects. It covers where to put apps, templates, static files, media, and settings so everything stays organized from day one.

  • You should already have myproject set up with the pages app and split settings in place.
  • This page is more of a reference than a step-by-step guide — apply what makes sense for your project size.

3. App structure

Each app should handle one specific feature or domain. Keep apps focused — if an app is doing too many unrelated things, split it.

Inside each app, add a urls.py file to keep the app's URL patterns self-contained:

pages/
├── migrations/
├── templates/
│   └── pages/
│       └── index.html
├── __init__.py
├── admin.py
├── apps.py
├── models.py
├── tests.py
├── urls.py       ← create this yourself
└── views.py

Notice the template is placed inside templates/pages/ — not directly inside templates/. This namespacing prevents template name conflicts between apps. For example, if two apps both have an index.html, Django would load the wrong one without this namespacing.

4. Templates

There are two places templates can live and both are valid:

  • Inside the apppages/templates/pages/index.html. Good for app-specific templates that are only used by that app.
  • Project-leveltemplates/base.html. Good for shared layouts, base templates, and templates used across multiple apps.

Add the project-level templates folder to your settings so Django can find it:

# mysite/settings/base.py

TEMPLATES = [
    {
        ...
        'DIRS': [BASE_DIR / 'templates'],
        'APP_DIRS': True,
        ...
    }
]

With APP_DIRS: True and DIRS set, Django searches the project-level templates/ folder first, then each app's templates/ folder. Put your base.html in the project-level folder and extend it from app templates.

5. Static files

Like templates, static files can live in two places:

  • Inside the apppages/static/pages/pages.css. Namespace it the same way as templates to avoid conflicts.
  • Project-levelstatic/css/, static/js/, static/images/. For shared assets used across the whole project.

Add the project-level static folder to settings:

# mysite/settings/base.py

STATIC_URL = 'static/'

STATICFILES_DIRS = [BASE_DIR / 'static']

STATIC_ROOT = BASE_DIR / 'staticfiles'  # used by collectstatic in production

6. Media files

Media files are files uploaded by users — profile photos, documents, attachments. They are kept completely separate from static files because they are dynamic and user-generated.

# mysite/settings/base.py

MEDIA_URL = 'media/'
MEDIA_ROOT = BASE_DIR / 'media'

The media/ folder is created at runtime when the first file is uploaded. Never commit the contents of media/ to Git — add it to .gitignore.

7. What goes in .gitignore

At minimum, your .gitignore should exclude:

# .gitignore

.env
.venv/
__pycache__/
*.pyc
*.pyo
db.sqlite3
media/
staticfiles/

8. Splitting large files

As apps grow, single files like models.py and views.py can become very long and hard to navigate. A common pattern is to convert them into packages:

pages/
├── models/
│   ├── __init__.py   ← import all models here
│   ├── page.py
│   └── category.py
├── views/
│   ├── __init__.py
│   ├── page_views.py
│   └── category_views.py

The key is to import everything in __init__.py so the rest of your code still works with the same import paths:

# pages/models/__init__.py
from .page import Page
from .category import Category

9. Next steps

Your project is now structured cleanly for growth. The next step is to configure static and media settings properly so Django can find and serve your files correctly in both development and production.


Never miss a story on Django.wiki

Subscribe for fresh tutorials, snippets, and updates.

By subscribing you agree to our Privacy Policy.