Here's an example recipe for a Django project layout:
src/
apps/
blog/
project/
settings/
templates/
asgi.py, urls.py etc..
manage.py
tests/
pyproject.yaml
That's the basic layout we're going to discuss now. But if you want to get started quickly, try running the prototype startproject.sh command that will bootstrap the above, with your project name as the parameter (source: GitHub repo).
And food recipe blogs, notice how we STARTED with the recipe... make note that you CAN actually serve the recipe BEFORE the word soup 😎 🥗
Whether your project is a quick'n'dirty one-off project or you're starting something with a longer trajectory, try to avoid stalling at universal decisions and instead use that time to design project-specific structures or simply start building. If your project is small or has low complexity, you can be happy with a simple structure like the above. If you expand to more complex needs, this is a good start, and there's a few more hints in this post about adding additional structure.
If you're new to Django and you don't know what exactly these various components in the structure are, there'll be some additional pointers in the below discussion of choices.
At the time of writing this, DEP-14 and DEP-15 were accepted. These are both proposals to change things in Django: The former proposes a new django command for everything, and the latter introduces django new sub-command that will prompt the user about which starter template to use for a new Django project. So let's say the trajectory of this blog post is also to open the floor on how such a template could look and behave 🌠.
Here is what the startproject.sh offers:
NB! (to be updated later) At the time of writing this, the owners of uv have sold themselves to OpenAI - this blog post is not an endorsement of future uv/Ruff
Ultimately (moonshot part 🚀🌕), we should let django new break new ground by combining its nice simple interface with a new tutorial that makes it easier for people to use and understand the combination of Django project and Python package.
🗯️ Comments, questions, thoughts? Please feel free to respond to the Fediverse announcement of this post.
This was intentionally made as a kind of baseline for a Django project. We will have a section for additional common problem and their solutions. But here are some explanations of the initial choices:
src/: We will put all our Python source code in a distinct folder, because your project WILL have other files and folders that aren't Python source code. You may or may not want to distribute a Wheel, but this is just a tidy and very simple rule. Polluting the root various packages is not what you want, trust me.src/apps/: We have apps and projects in Django, those are very important distinctions that should trigger a Django developer to engage in long philosophical pondering. So in order to reflect this understanding in the folder layout, let's avoid the conflation of apps and projects (or actually "project" in singular).src/project/: Almost the same thing, except see what I did here? I made the main project simply be called project, in singular. Yes, it could have been django_project or site or <organization_name>. But ultimately there is just one project and it's the Django project ☺ By simply calling it "project", we maintain the terminology of what makes a project be different from all your app domains, and no one needs to wonder "is this an app?" - no, it's a project!src/project/settings/: You will of course need a settings module, otherwise your Django project cannot run. But from the inception of your project, make it a package so you can have a variety of settings for each of your fundamental environments (often that's development and production, maybe test and staging).src/project/templates/: You should always anticipate at least on project-wide templates folder because there are templates that form the base of all your apps. Otherwise, you will end up storing project-related templates in your apps.src/manage.py: This is the main entrypoint of it all, it could be in the root folder, but let's treat it as a part of the main src that should be installed on the target. This makes it possible to ultimately create a Python package and point to manage.py with an entrypoint. Do you wanna run acme runserver inside your VPS or AWS task or Docker Container? Sure thing, just configure the pyproject.yaml with these aliases.tests/: Here is a very very important simple step: Always keep tests separate. We will get into how we can organize tests later. But do not mix them with the actual project, tests should be observing their subject (the application/project/software) from outside.pyproject.yaml: Python in 2025 pretty much makes it possible to manifest every configurable aspect of your project in this toml file, so let's stick to that. This also means we can operate uv from the beginning to do things quickly, like managing your environment and adding more dependencies to your Python project.It's possible that other tools such as environment and project managers like uv (or pip, hatch , etc - and combinations) and pre-commit (or the recent prek) will also fit very well when bootstrapping a project. Some people would even say that Docker Compose is a fixed ingredient in all their project recipies. Aside from that, there are commonly used Django dependencies like psycopg(3) and django-debug-toolbar. It means that we might have A LOT of considerations to present in a template or a tutorial. But these are more like variables, unlike the basic structuring of the Django project itself. Django itself can have more opinions about how to get people started, which is what the new django new command will offer.
This very adjacent to the idea of "batteries included": Get people started with developing their web project, including pytest, pre-commit, ruff and uv.
src/apps/ vs. src/project/
The following sentence is not very intuitive: We will stop thinking that a Django project consists of apps - the apps actually live outside of the project scope itself. That means we need to consider what a project really is, and what it means if the apps are not included? Well, a Django project is a central, single unit that combines and configures a set of apps. Those apps might be very targeted to work in the project, but ultimately, they are standalone Python packages - and even if their usage outside of project is unthinkable, we still maintain that position! The unintuitive part here will serve to help us define and design each app.
So you have these layers:
We're not gonna mix these layers, so now we can stop worrying about Django apps somehow being inside a Django project. Because that is confusing! (Sorry to the people advocating the single-folder django app & project combination)
Remember also that outside of these layers, we also have "the IDE project", "the Git project", "the Docker Compose project" etc. So people are more or less familiar with the ambiguity of the word "project" - or let's just call it an inevitable condition.
Apps each have a domain: This is taken from good old domain-driven design. This should avoid the apps from overlapping, making it easy to decide what goes where, and avoiding circular dependencies. Apps may depend on each other one-directionally, but you should always try to make it so that dependencies aren't two-way (this might be impossible in some rare occasions). The user app is there from the inception as a good practice: You should probably always have your custom user model and avoid having anything in this app's domain intersect or depend on other apps. If you have a shop app, it will depend on the user app, but the user app should not depend on shop!
Discussing app domains is a positive problem. It helps you and your team understand your problem better. If your app domains are hard to define, it's very likely that the solution needs a bit more work - and hopefully if this is hard, it's also a fun challenge!
We all want APIs now and then, but do we create one API per app? Or a common API for all our custom apps?
Let's think for a moment: We have the Python project containing the Django project and a set of stand-alone Python packages. Do we have a simple API for each app? If that makes sense to you, you should definitely define APIs inside each Django app - maybe inside a sub-package like blog.api with nested views and serializers (if you're using Django Rest Framework).
But if this does not immediately make sense to you, my recommendation is to start differently:
Create a Django app simply called "api".
So you get:
src/
apps/
api/ # <- A unified app depending on all the other apps included in API views
blog/
project/
From this starting point, you have both the option to try to keep your APIs separate for each app domain or allow the intersection. But even better, you will have a mirrored solution in your tests where you can target all your API tests on this particular app. That can help to structure your project in general, not least since most APIs share a common codebase around authentication, rate limiting, caching etc.
If you're using your Django project as a fullstack project with templates, then template structures are very important. You don't want templates and templatetags to be inheriting across apps. The templating language has less support from IDE and linting, so it's important to try to stick to something that doesn't become complex.
The suggestion here is to ALWAYS have a templates directory for your project and to create a "base.html" here that each app will inherit from. Even if you're not targeting a distributed, reusable model, you should still organize these things in a neat, intuitive way. This makes it easy for other developers to guess your structure's logic.
src/
apps/
blog/
templates/
blog/
base.html # extends from "base.html"
project/
templates/
base.html # your whole site's base template
This is quite similar for static files.
As a baseline, we should always use environment variables, but there are a few things that aren't well-suited. But instead of unlimited settings permutations, we assume that there are a very few selected, eligible settings.
We ALWAYS want a separate development settings module. This will not only include DEBUG=True but also configure things like django-debug-toolbar, maybe a different database configuration, maybe eager triggering of Celery tasks - who knows. The point here is to AVOID following the 12-factor model strictly and end up with a jungle of environment variables. You may argue that it's also 12-factor to specify DJANGO_SETTINGS_MODULE in your deployment.
The main caution here is to avoid drifting. This can lead to developers to make wrong assumptions about the production environment because their development environment is too different.
We are intentionally NOT including the otherwise populare "settings/local.py" because this is often a source of developer bugs. A developer may have different settings from the rest of the team, and suddenly there are two sources that have to be debugged: The environment variables AND a local.py.
Like views or models, tasks are a very specific kind of object in Django. It's a function that receives a set of serializable paramenters and perfoms its functionality from only that context. It happens outside of the request-response loop. It's important to scope tasks so they are easy to test and maintain. The conclusion here is to keep your tasks bound to each app's domain and have for instance:
src/
apps/
blog/
tasks.py # <- all tasks within the blog domain
project/
Again, if you have dependencies between apps, make sure they only depend in one direction. A new shop app can depend on a users but not the other way. So you can have shop.tasks doing shop-related operations with users, but then you should not put shop-related things in users.tasks! Whenever app domains and their dependencies become tricky or complex, it hints towards defining new apps.
If you read this far: You might be inclined to skip tests in the beginning of bootstrapping your project, but you will need them soon. And the worst kind of test setups are the ones that don't distinguish between their various genres: e2e, integration and unit testing.
A good goal for your layout is to eventually create one test package per app. And you might as well also start out like that.
src/
apps/
blog/
project/
tests/
conftest.py # <- Here we have global test fixtures
blog/
conftest.py
e2e_tests.py # <- test your views here and let everything execute. Few but expensive.
unit_tests.py # <- test individual functions
This is really just an example: You might also want to create view_tests, model_tests etc. But still make sure that you can grasp the different genres of tests:
The idea here is still to ensure that you design every test case with some understanding of its scope: You should not test a simple model method by calling a view, and you should not write loads of assertions on the internals of a model method if you're actually testing the view.
This all boils down to finding the value and motivation for writing tests. Try to find yourself doing test-driven development as soon as possible, but if it doesn't make sense, don't do it... yet... but still, at least squeeze in some e2e tests from the beginning, they usually provide a payoff quite quickly
Enough about testing, it's a whole category of its own 🤪
users app and AUTH_USER_MODEL?Django provides a simple User model from django.contrib.auth. However, once you have started using it, migrating to your own custom model becomes tricky and most people resort to having a relation to some kind of UserProfile model.
That's why I recommend to start all projects with your own users app and define settings.AUTH_USER_MODEL to point to that model. You can read more about this in the Django docs: https://docs.djangoproject.com/en/5.2/topics/auth/customizing/#specifying-custom-user-model
In order to reduce how opinionated this project layout is, I kept it out, and I think these sorts of choices are more appropriate for a tool like cookiecutter.
I've seen many examples where a Django application have for instance a whole package app.models full of nested models, each with their own domain. Having domains inside the app domain is easily a sign that you need a new separate app, but it can also be a sensible and intuitive way to split up models and views. Maybe you have models.abstract or views.api, views.public etc.
I'd prefer my models.py to be lengthy, rather than having to wrestle around with __init__.py imports and from .models.submodule import ModelName.
It's outside of the scope here to discuss the idea of service layers. I might discourage it, and I certainly don't feel like something like an apps/blog/service.py would be appropriate in a template.
It makes sense to have a common or shared app, but seeing that it's not likely to have any concrete models, it's best to place it outside of your apps (so it's not a Django app) and project (since it's not really part of the Django project). That could look like this:
src/
apps/
project/
shared/
Not far from this discussion maybe: it's true that backend API projects could discuss whether they live alongside the frontend etc. I
There are some drawbacks to using these other names:
config or conf: I think that config/templates is wrong, if we allow an HTML file to be considered "configuration", then anything is configuration or we have to invent a NEW location for project-wide templates, static data, backends etc. It's not a terrible convention, definitely better than nothing. It's used by the project django-new for bootstrapping Django projects and apps.site could also work but I think project works much better because we already have a concept of a "Django project", whereas a "Django site" would be something completely new and without clear advantages.tests/ separate from src/?In this article, we knowingly recommend to maintain all test-related code in its own tests/ folder/Python package.
Maybe you've seen layouts where Django applications each have their own app/tests/ folder. This is a bad idea because your tests will be built into your application. If you have test fixtures, they will be loaded into your deployed application image. If you do a mistake, the entire test stack might be inspected/imported.
A common "lazy" mistake of maintaining test code together with the application, is to start mixing test requirements with application requirements, too. This could ultimately result in pytest or playright ends up in production images.
I would propose that anything you extend and override from inside Django is placed in your src/project/ folder. We can see it as a kind of configuration. In certain cases (such as authentication) you might have an app's domain fit perfectly, but in most cases, you need a one-off customization that fits well in src/project/.
Using template partials to avoid lots of files:
https://indieweb.social/@adamghill/115329417459835209
Against service layers in Django
https://www.b-list.org/weblog/2020/mar/16/no-service/
Why do we need apps?
https://forum.djangoproject.com/t/why-do-we-need-apps/827/28
HackSoft's Django Style Guide
https://github.com/HackSoftware/Django-Styleguide