Django Best Practices: Projects vs. Apps

Updated

Table of Contents

Within a single Django "project" can exist multiple "apps," which are used to separate discrete functionality. For example, a Django website might need an application for static pages like an About page, an app for payments, an app for blog posts, etc. The project/app structure gives the Django developer complete control and some handy structure for reasoning about code.

Django also has a rich ecosystem of third-party packages that provide additional functionality and sometimes eventually make their way into core Django. You can see a complete list on the djangopackages.org site or a more curated list at the awesome-django repo.

Initial Set Up

Let's start by creating a new Django project, which assumes you already have Python installed on your computer and have a basic knowledge of the command line.

Navigate to a new directory--we're using one called code here on the Desktop--and create a new virtual environment. Then install Django using pip.

# Windows
$ cd onedrive\desktop\code
$ mkdir pages
$ cd pages
$ python -m venv .venv
$ .venv\Scripts\Activate.ps1
(.venv) $ python -m pip install django

# macOS
$ cd ~/desktop/code
$ mkdir pages
$ cd pages
$ python3 -m venv .venv
$ source .venv/bin/activate
(.venv) $ python -m pip install django

If you use the command runserver, the local Django web server will start and display the Django welcome screen at http://127.0.0.1:8000/.

(.venv) $ python manage.py runserver

Django welcome page

Project

A project is a web application using Django. There is only one project and many "apps" within it. So for our blog web application, we need to create it and assign a name like django_project.

(.venv) $ django-admin startproject django_project .
(.venv) $ tree
.
├── django_project
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
└── manage.py

Note I've added the optional period . at the end of the command so the files are included in the current directory. Otherwise, Django would automatically create an additional directory with our project name and then add the starter files within that directory, which seems redundant to me, but some developers like that approach.

INSTALLED_APPS

Within the newly created settings.py file is a configuration called INSTALLED_APPS, a list of Django apps within a project. Django comes with six built-in apps that we can examine.

# config/settings.py
INSTALLED_APPS = [
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
]

Apps

A Django app is a small library representing a discrete part of a larger project. For example, our blog web application might have an app for posts, one for static pages like an About page called pages, and another app called payments to charge logged-in subscribers.

We can add an app using the startapp command, so let's add the posts app now. We can see our complete updated structure using the tree command.

(.venv) $ python manage.py startapp posts
(.venv) tree
.
├── django_project
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── manage.py
└── posts
    ├── __init__.py
    ├── admin.py
    ├── apps.py
    ├── migrations
    │   └── __init__.py
    ├── models.py
    ├── tests.py
    └── views.py

The posts app has been created and has its associated files. You'll often want to add a urls.py file here, too.

We also must add the app to our INSTALLED_APPS setting, or else the Django project won't recognize it.

# django_project/settings.py
INSTALLED_APPS = [
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
    "posts",  # new
]

Deciding on what constitutes an "app" is necessarily subjective. Could we combine all the logic for our posts and payments into one app? Yes, we could. Does that make it easier to reason about as our project grows? I'd argue no, but again, it's subjective. You'll see various approaches to app design out in the wild, but the general best practice is the same: each Django app should do one thing and one thing alone.

3rd Party Packages

A 3rd party package is a plain old Django application that has been designed to be pluggable into any existing project with the Python packaging tools. You can see a tutorial on this here. It takes just a few additional steps to separate functionality into smaller apps. It's far easier to share them within a larger project, within a company, or to make them public like a 3rd Party Package.

App Naming Conventions

An app's name should follow Pep 8 Guidelines. Namely, it should be short, all-lowercase, and not include numbers, dashes, periods, spaces, or special characters. It also, in general, should be the plural of an app's main model, so our posts app would have a main model called Post.

Conclusion

Django apps may seem overkill when starting, but they provide much-needed structure and flexibility to any Django web application. Further, the ability to separate one, if desired, has led to a robust ecosystem of Django 3rd Party Packages that improve the overall community immensely.