Fun with Learning Technology
LearnCoursesQuestionsTracksToolsNewsExplorePractice
Fun with Learning Technology

A new problem, explained clearly, every day.

Subscribe
Learn
  • Lessons
  • Topics
  • News
  • Tools
  • Courses
  • Career tracks
  • Everything
Site
  • About
  • Contact
  • Support
  • Privacy
  • Terms
Get the daily one

One email per new problem. No spam.

Request a tutorial

Requests shape what gets made next.

© 2026 Fun with Learning TechnologyRSS
Home›Courses›Django›Project and App Structure

Architecture

Project and App Structure

Django projects are organized into a monolithic configuration container and multiple modular, pluggable application components. This separation allows developers to isolate domain logic into discrete units that can be reused across different installations or shared within a team. Understanding this structure is essential for scaling codebases and maintaining clean separation of concerns as feature sets grow in complexity.

The Project Container

A Django project serves as the global configuration wrapper for your application. When you run the startproject command, Django generates a directory containing essential files like settings.py and urls.py. The project-level configuration acts as the master control room for your entire ecosystem. It does not contain business logic; instead, it defines how the environment behaves, managing database connections, middleware stacks, security keys, and internationalization settings. By isolating this configuration from individual apps, you ensure that the core behavior of your system remains consistent regardless of which specific features are active. This separation allows you to manage dependencies and system-wide constants in one centralized location. If you place business logic inside the project files, you effectively lock that logic into a single context, violating the principle of modularity that Django encourages. Always keep the configuration clean and delegate functionality to modular apps.

# settings.py - The central configuration hub
import os

# Defining the base directory for the project
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

# System-wide settings applied to all apps
INSTALLED_APPS = [
    'django.contrib.admin',
    'myapp', # Custom apps are registered here
]

# Security and database parameters central to the project container

Anatomy of an App

An app is a self-contained unit of functionality designed to perform a specific set of operations, such as managing user profiles or handling an e-commerce catalog. Each app lives in its own subdirectory and includes its own models, views, templates, and tests. The rationale for this design is encapsulation: when an app is fully decoupled, you can migrate it between different projects by simply copying the directory and updating the INSTALLED_APPS list. This promotes a 'plug-and-play' architecture where each component has a clear boundary. By grouping related files—like database schemas in models.py and request handlers in views.py—within an app, you reduce cognitive load for developers because they do not need to hunt through a global file system to modify a specific feature. This granular approach is the secret to building large, maintainable systems that do not turn into unmanageable, spaghetti-code monoliths over time.

# myapp/models.py - Encapsulating feature-specific data
from django.db import models

class Product(models.Model):
    # The model defines the data structure for this specific app
    name = models.CharField(max_length=100)
    price = models.DecimalField(max_digits=10, decimal_places=2)

    def __str__(self):
        return self.name

Managing URLs and Routing

Routing in Django is hierarchical. The project-level urls.py serves as the root router, which typically directs traffic into individual apps using the include() function. This hierarchical structure is vital for scalability. By delegating path handling to an app-specific urls.py file, the project router remains uncluttered, only needing to know the entry point for each feature. This approach facilitates namespace management and prevents route collision between different developers working on different modules. If you were to keep all routes in a single file, the complexity would grow linearly with the number of features, leading to merge conflicts and extreme fragility. Instead, each app declares its own path patterns, effectively owning its portion of the application's URL space. This ownership model allows for cleaner, more predictable URL structures and simplifies the process of creating nested navigation logic without tightly coupling separate application modules.

# project/urls.py
from django.urls import path, include

urlpatterns = [
    # Routing traffic to the app's internal URL configuration
    path('products/', include('myapp.urls')),
]

# myapp/urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('', views.product_list, name='list'),
]

Decoupling Through Middleware

Middleware acts as a plugin system that processes requests and responses globally before they reach or leave your apps. By sitting between the server and your application code, middleware can intercept data to add authentication, logging, or security headers. This is a critical architectural pattern because it keeps common logic out of your views. If you had to manually verify user sessions in every single view, your code would become bloated and prone to errors. Middleware solves this by providing a hook into the request-response lifecycle at a structural level. It is important to note that the order of middleware is significant, as each layer processes the request sequentially. By utilizing this layer, you maintain a 'dry' (Don't Repeat Yourself) codebase where cross-cutting concerns are handled transparently, allowing the individual apps to focus exclusively on their core business domain.

# A simplified example of custom middleware logic
class SimpleMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        # Logic executed before the view is called
        response = self.get_response(request)
        # Logic executed after the view is called
        return response

Standard Directory Layouts

Following a standard directory layout is essential for predictability. Django expects files like models.py, views.py, and admin.py to reside at the root of your app folder so that the automatic discovery system can find them. Straying from these conventions creates unnecessary friction and makes it harder for automated tools or new team members to understand the codebase. While Django is flexible, the 'Django way' suggests that your projects should include a requirements.txt file at the root, a manage.py script for interface interaction, and consistent naming conventions for your app directories. This standard allows developers to immediately understand where to look for data logic versus presentation logic. By adhering to these structures, you ensure that your project remains compatible with the wider ecosystem of reusable third-party packages, making it easier to integrate functionality without reinventing basic infrastructure for every new application requirement.

# Standard file structure overview
# /project_root
#    /manage.py
#    /config_folder/ (settings, wsgi, asgi)
#    /my_app/
#        /models.py
#        /views.py
#        /apps.py

# Ensure manage.py is always at the root for standard execution
# python manage.py runserver

Key points

  • A project serves as the global container for configuration and environment settings.
  • Individual apps should be self-contained modules to maximize code reuse.
  • Separating configuration from business logic prevents tight coupling of features.
  • Hierarchical routing via include() simplifies project navigation and prevents name collisions.
  • Middleware provides a global hook for request processing without polluting view logic.
  • Adhering to standard directory layouts ensures compatibility with Django discovery tools.
  • Every app should encapsulate its own data models and associated business rules.
  • Strategic use of apps allows for independent scaling of different functional system parts.

Common mistakes

  • Mistake: Putting application-specific logic directly into the 'settings.py' or 'urls.py' of the project root. Why it's wrong: This breaks modularity and makes the project difficult to maintain. Fix: Keep project-level files for configuration only and delegate logic to individual apps.
  • Mistake: Defining models in the root project folder instead of inside a specific app. Why it's wrong: Django's ORM relies on the app registry; models must belong to an app to be detected and migrated. Fix: Move models into an app's 'models.py' and ensure the app is in 'INSTALLED_APPS'.
  • Mistake: Importing absolute paths for app modules throughout the project. Why it's wrong: This creates circular dependencies and tight coupling that prevents app reusability. Fix: Use relative imports or proper dotted path notation based on the project structure.
  • Mistake: Overloading the 'views.py' file with database queries and business logic. Why it's wrong: It makes the code harder to test and violates the fat-model, thin-view principle. Fix: Move complex data processing into a 'services.py' or onto model methods.
  • Mistake: Placing static and media files in the project root without proper configuration. Why it's wrong: Django requires explicit 'STATIC_URL' and 'MEDIA_URL' mappings to serve assets correctly in different environments. Fix: Configure these paths in 'settings.py' and use the 'staticfiles' app.

Interview questions

What is the role of the settings.py file in a Django project?

The settings.py file is the central configuration hub for a Django project. It contains essential settings such as database connections, installed apps, middleware, template configurations, and security keys. Its role is critical because it tells Django exactly how to behave in different environments. By centralizing these configurations, Django ensures that environment-specific variables like DEBUG mode or ALLOWED_HOSTS are easily manageable without altering the core application logic, which promotes clean architectural separation.

What is the purpose of the 'manage.py' script and how is it used?

The manage.py script is a command-line utility that comes automatically with every Django project. It acts as an interface to the project, allowing developers to execute various management tasks such as running the development server, performing database migrations, creating superusers, or executing custom management commands. Essentially, it serves as a wrapper around the Django-admin tool, pre-configured with your project's settings.py path, ensuring that all commands run within the context of your specific project environment.

Can you explain the difference between a Django project and a Django app?

In Django, a project represents the entire web application, including configuration and settings that apply to the whole system. An app, conversely, is a self-contained module that performs a specific function, such as a user authentication system or a blog engine. The advantage of this structure is reusability; you can develop an app in isolation and plug it into different projects. A project is simply a collection of these decoupled, specialized applications.

What is the function of the 'urls.py' file and why does Django use a project-level vs app-level url configuration?

The urls.py file is the URL dispatcher that maps request URLs to specific view functions. Django encourages a hierarchical structure: project-level URLs act as a 'router' that redirects requests to the appropriate app's urls.py file. This separation of concerns is vital because it allows apps to be modular. By defining URL patterns within each app, the app remains portable. You can include an entire app's URL namespace in a new project just by adding one line to the main urls.py.

Compare placing business logic in views versus placing it in models or custom utility modules.

Placing business logic in views leads to 'fat views' and violates the DRY principle, making code hard to test and maintain. It is better to place logic in models (using model methods) or dedicated utility modules. Models should handle data-related logic, while utility modules handle shared calculations. By moving logic out of views, you ensure that functions can be reused across different parts of the application, significantly improving testability and code readability.

Explain the significance of the 'AppConfig' class and the 'apps.py' file in larger Django projects.

The apps.py file contains the AppConfig class, which provides metadata for a specific application. This class is essential for larger projects because it allows you to control how an app is initialized, register signals, or perform setup tasks when the project starts. For example, by overriding the 'ready()' method, you can connect signal handlers reliably. It essentially serves as an entry point for configuring app-specific behaviors that need to be triggered during the application lifecycle.

All Django interview questions →

Check yourself

1. What is the primary architectural purpose of splitting a Django project into multiple apps?

  • A.To allow different developers to work on different files simultaneously.
  • B.To logically encapsulate specific features, making them reusable across different projects.
  • C.To increase the speed at which the Django development server restarts.
  • D.To force the database to use separate schemas for each application.
Show answer

B. To logically encapsulate specific features, making them reusable across different projects.
The correct answer is encapsulating features for reusability. The other options are incorrect because multi-app structures are about code organization and modularity, not about performance tuning, database schemas, or file-locking conflicts.

2. Why is it mandatory to include a newly created app in the 'INSTALLED_APPS' list within 'settings.py'?

  • A.To inform the Django template engine about the existence of the app's templates.
  • B.To ensure the development server recognizes the folder as a valid directory.
  • C.To allow Django to detect the app's models, migrations, and template tags.
  • D.To force the project to build a separate database table for the app's configuration.
Show answer

C. To allow Django to detect the app's models, migrations, and template tags.
The correct answer is to allow Django to detect models and migrations. Without this, Django does not load the app's configuration, so the other options are wrong because they describe functionality that does not depend on the app registry or is simply false.

3. Which of the following describes the correct role of the project-level 'urls.py' versus an app-level 'urls.py'?

  • A.The project 'urls.py' handles all business logic, while the app 'urls.py' only handles static files.
  • B.The project 'urls.py' acts as a central router that includes app-specific URL patterns.
  • C.The project 'urls.py' must define every URL path in the entire project.
  • D.The app 'urls.py' is used to override the project's global security settings.
Show answer

B. The project 'urls.py' acts as a central router that includes app-specific URL patterns.
The project 'urls.py' acts as a central router. The other options are wrong because they misrepresent the delegation of concerns: apps should manage their own paths, and the project root should never hold the entirety of the project's routing.

4. If you are following the Django 'fat-model, thin-view' principle, where should you ideally perform data validation that involves multiple fields of a model?

  • A.Inside the view function before saving the model instance.
  • B.Inside the template using conditional tags.
  • C.Inside the model's clean method or a custom save method.
  • D.Inside the project's 'settings.py' file.
Show answer

C. Inside the model's clean method or a custom save method.
Validation belongs in the model because it ensures data integrity regardless of where the data is updated. Views, templates, and settings are inappropriate for model-level business logic, making those options incorrect.

5. What is the consequence of placing a 'templates' directory at the root of your project instead of inside an app folder?

  • A.Django will automatically search the root for templates before app folders.
  • B.Django will crash immediately because it expects templates to be in apps.
  • C.The template will not be found unless specifically configured in 'TEMPLATES' settings.
  • D.It increases the loading speed of the templates significantly.
Show answer

C. The template will not be found unless specifically configured in 'TEMPLATES' settings.
Django defaults to looking for templates inside app directories; a root folder must be explicitly added to the 'DIRS' list in 'TEMPLATES' to be found. The other options are wrong because they assume automatic behavior or claim impossible results like faster performance.

Take the full Django quiz →

← PreviousDjango MTV ArchitectureNext →Settings — BASE_DIR, Installed Apps, Middleware

Django

30 lessons, free to read.

All lessons →

Track your progress

Sign in to mark lessons done, score quizzes and keep notes.

Open in the app