Django Project Structure

March 21, 2025 • Django

Now that I have completed a few projects using Django I wanted to create a consistent framework for building maintainable web applications.

I prefer to work at establishing a consistent project structure across all of my projects because I work better that way. I also believe ti will significantly improve my workflow, reduced setup time, and make maintenance much more straightforward. In this article, I'll share my recommended Django project structure that embraces the "apps are pluggable" philosophy while incorporating other Django best practices.

Django Project Structure

Thanks, for sharing:

1. The Configuration App: "config"

I will name my main Django configuration app "config" instead of using the project name. This provides immediate clarity that this module handles project-wide configuration, not application logic.

# config/local_settings.py - Environment-specific settings

DEBUG = True

ALLOWED_HOSTS = ['localhost', '127.0.0.1', 'localhost:8000']

I am currently using MySQL as the database and MariaDB specifically, so I create a new local database for the project.

To ensure I can see the images I add this to the main project urls.py file:

if settings.DEBUG:

    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

# config/settings.py - Main settings file

import os from pathlib

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

SECRET_KEY = 'your-secret-key'

ALLOWED_HOSTS = ['domainname.com']

CSRF_TRUSTED_ORIGINS = ["https://domainname.com"]

DEBUG = False

# Production Database configuration

Once I have set up the project on my hosting panel I createa MariaDB and include the settings in my .env file.

# Email settings

# Other production/development-specific settings especially relating to media and /static/

This separation allows me to maintain different settings for development and production environments without complex settings packages.

2. Core App for Common Functionality

I always create a "core" app that contains project-wide functionality. This includes:

  • The main homepage

  • Base models that others might inherit from

  • Utility functions/classes used across the project

  • Project-wide middleware and template tags

Having this core app means I never need to decide where to put general functionality that doesn't fit within a specific feature app.

3. Fat Models, Thin Views

I follow Django's principle of keeping business logic in models and keeping views simple. By putting my business logic in the model, it can be reused anywhere, from views to templates to API serializers.

4. Custom Model Managers

For complex or frequently used queries, I implement custom model managers. This approach keeps query logic consistent and avoids duplicating complex filters across multiple views.

5. App-specific Templates

I organise templates within each app to ensure they remain portable. I include double nesting by including the name of the app inside the templates folder. So the structure will go app (blog) with a folder called templates. Inside that templates folder is another folder called blog. Inside that blog folder are all the template files.

blog/

   └── templates/

      └── blog/ # Note the double nesting

            ├── post_list.html

            └── post_detail.html

This double nesting (app name within templates) ensures that when I include an app in a different project, its templates don't clash with other apps.

6. Form and Serializer Validation

I keep validation logic in forms or serializers, not in views. This centralizes validation logic and keeps views focused on handling HTTP requests, not data validation.

7. Class-Based Views

I use class-based views for consistency and reusability. Class-based views encourage consistent patterns across the application and make it easier to extend functionality through mixins.

8. Admin Customisation

I keep admin customisations in a separate admin.py file in each app, which is pretty standard for most django applications. This keeps the admin interface clean and organised, making it easier for content managers to work with the site.

9. Signals in Dedicated Files

I always place Django signals in a separate signals.py file and import it in the app's apps.py. This organisation makes it clear where signal handlers are defined and helps avoid circular import issues.

10. URL Namespaces

I always use URL namespaces to prevent name collisions and make apps more portable:

app_name = 'blog' # This establishes the namespace

With namespacing, I can reference URLs in templates with {% url 'blog:post_detail' post.slug %} and in views with reverse('blog:post_detail', kwargs={'slug': post.slug}).

11. Services for Complex Business Logic

I haven't had to use this yet but for complex business logic that spans multiple models or involves external systems, I will create service modules. This helps separate different concerns and prevents models or views from becoming too bloated.

12. Consistent API Structure

When implementing Django REST Framework, I organise API endpoints consistently using a layered approach of seralizers, viewsets and URL routing. This layered organisation (serializers, viewsets, and URL routing) keeps API code neatly compartmentalised, making it easier to maintain, extend API functionality, and ensure proper separation of concerns while following Django REST Framework's recommended patterns.

13. Other Files Needed

Once the project is ready to deploy there are two other files I will need. A passenger_wsgi.py file and a .htaccess file.

If I have been working with Tailwind CSS in local development using their CDN link I will also update the project to include Tailwind CSS.

This consistent project structure has several advantages:

  1. Predictability: I know exactly where to find specific code in any project

  2. Reusability: Apps are truly pluggable and can be moved between projects

  3. Maintainability: Separation of concerns makes debugging and extending easier

  4. Collaboration & Onboarding: Should I ever work in a team or hire a developer this structure will work more efficiently with a shared structural understanding and ew developers can quickly understand the project organisation

By following these patterns across all of my Django projects, I will reduce development time, improve code quality, and make maintenance much more straightforward. While Django is flexible enough to accommodate many different structural approaches, using this approach works for me.

The key to a good project structure isn't just following a template—it's about understanding why each component is organised a certain way and how it supports Django's philosophy of clean, maintainable, and reusable code.

Thanks, for sharing:


© 2024 Djangify. All rights reserved.