Django: Laying out projects
A brief glance at how I like to maintain stuff, and possibly, the better way.
What’s the optimal layout for Django applications, configuration files and it’s associated directories?
I use the following directory structure. It is very opinionated, but I’ve worked upon it for so long and seems to be the most logical one for projects both small and large.
└── project_name # top level, everything under here is in git repo
│ └── app_name
│ ├── admin.py
│ ├── forms.py
│ ├── models.py
│ ├── templates # app specific templates
│ ├── urls.py
│ └── views.py
├── requirements # dependencies for the project: prod, test etc
├── docs # documentation, if any
├── project_name # inner project directory
| ├── urls.py
| ├── views.py
| └── wsgi.py
├── scripts # CI scripts, manage.py, etc
├── settings # settings for the project: prod, test etc.
| ├── base.py
| ├── prod.py
| └── test.py
├── templates # top level templates
└── tests # tests specific to every app
Note: Starting from Django 1.11, we get a new application configuration using the full path of
AppConfigderived classes within the Django app, as specified here.
Current default hierarchy
Kickstarting an example project foo using
django-admin startproject foo , we get a default directory structure like this:
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
This layout is a great starting place, we have a top level directory foo which contains our
manage.py the project directory foo/foo/ inside it. This is the directory you would check into your version control system such as git.
To fix bad settings, we’re gonna prefer usage of multiple environments with imports from
base.py . Suppose we have three environments, viz. test, staging, and production.
foo/make a settings directory and create an empty
__init__.pyfile inside it.
- Create the individual
foo/foo/settings/. Each of these 4 environment specific files should simply contain the following:
from base import *
So why is this important? Well for local development you want
DEBUG=True, but it’s pretty easy to accidentally push out production code with it on, so just open up
foo/foo/settings/production.py and after the initial import from base just add
DEBUG=False. Now if your production site is safe from that silly mistake.
What else can you customize? Well it should be pretty obvious you’ll likely have staging, test, and production all pointing at different databases, likely even on different hosts. So adjust those settings in each environment file.
Using these settings is easy, no matter which method you typically use. To use the OS’s environment you just do:
And boom, you’re now using the test configuration.
Anything else to be changed?
Yes. Another useful tip is the usage of lists instead of tuples, this way we can reuse configuration in other setting files. It’s often better to break up apps into two lists, one as prerequisites and other for actual applications within the project.
PREREQUSITE_APPS = [
PROJECT_APPS = [
INSTALLED_APPS = PREREQUISITE_APPS + PROJECT_APPS
Why is this useful? For one it helps better distinguish between Django core apps, third party apps, and your own internal project specific applications. However,
PROJECT_APPS often comes in handy as a list of your specific apps for things like testing and code coverage. You have a list of your apps, so you can easily and automagically make sure their tests are run and coverage is recorded just for them, not including any third party apps, without having to maintain the list in two separate places.
For small projects, it’s an easy way to get all your URLs in one place at
foo/urls.py . But if you’re willing to reuse apps, you’ve define them each in their own app. So, instead of:
urlpatterns = [
url(r’^$’, HomePageView.as_view(), name=‘home’),
url(r’^blog/$’, BlogList.as_view(), name=‘blog_list’),
url(r’^user/list/$’, UserList.as_view(), name=‘user_list’),
we should do:
urlpatterns = [
url(r'^$', HomePageView.as_view(), name='home'),
Use project paths
By default, django apps within the project have to be present within the root directory.
AppConfig derived classes have a name attribute useful for pointing the exact location of the application.
- Create an
- Create an empty directory
foo/apps/barfor bar django app.
- Kickstart the app by using
django-admin startapp bar foo/apps/bar
- Now change the contents of
from django.apps import AppConfig
name = 'apps.bar'
5. Use the full app config path in
INSTALLED_APPS = [
That’s it, you’re good to go and show off your new and better Django layout.
In relation to GitMate and Google Summer of Code 2017, I followed these steps at code.gitmate.io to migrate all gitmate plugins into