https://itnext.io/best-practices-for-structuring-a-django-project-23b8c1181e3f

Best Practices for Structuring a Django Project | by eyong kevin | ITNEXT

[

eyong kevin

](https://medium.com/@tonyparkerkenz?source=post_page—byline–23b8c1181e3f——————————–)

[

ITNEXT

](https://itnext.io/?source=post_page—byline–23b8c1181e3f——————————–)

Best Practices for Structuring a Django Project

In this article, I’ll walk you through a boilerplate I created for my Django projects. It uses best practices and will permit you to build industrial standard Django projects.

Instead of struggling with your Django project structures. Rather, spend your energy building exciting features.

It has received much appreciation from my peers and students from DCI and CF.

NB: Check my Django boilerplate from github

We will cover the following;

  • Starting with a virtual environment
  • Creating Django Project
  • Requirement
  • Settings
  • Django Applications
  • Command shortcuts with Makefile

Starting with a virtual environment (VE)

A Virtual Environment is a lightweight Python installation with its own package directories

Installing packages globally usually requires elevated privileges such as root, sudo, etc. When this is done, the package setup.py is executed with superuser privileges. This is a huge security risk if the package contains malware

Also, the command pip isntall -U <package> installs and updates the package and all its dependencies. This may mess with the existing packages or even break your other projects.

Lastly, installing everything globally pollutes the list of packages, making it hard to keep track of specific project’s dependencies.

Creating a VE and Install Django

Since Python 3.6, we can create VE with the command

python3 -m venv <folder-name> [--prompt <ve-name>]

NB: In Ubuntu, we must install python3-venv via apt . This is because venv is excluded from the default Python installation in Ubuntu

Another alternative would be virtualenv which can be installed globally with pip install virtualenv

Let’s now create a virtual environment for our Django project. In a folder called Django_First , open a terminal, and run the command

python -m venv .venv --prompt django-first

After we have created our virtual environment, we need to source or activate it before installing any packages.

source .venv/bin/activate

Then, install Django. Here I am using ~= to install the newest version within the same major release, which is, 5in the case below.

pip install Django ~=5.0.0

Creating Django Project

To create a Django project, we use the command django-admin and subcommand startproject . Then we specify some folders after that.

django-admin startproject <project-name> [<config-folder-name>]

Say, we want our project name to be djangofirst . Many newbies will create it like this

django-admin startproject djangofirst

This will be the structure of your project if you do it as above;

.
├── Django_First
│   └── djangofirst
│       ├── djangofirst
│       │   ├── __init__.py
│       │   ├── asgi.py
│       │   ├── settings.py
│       │   ├── urls.py
│       │   └── wsgi.py
│       └── manage.py

We can notice a lot of indented folders and some with the same name. In this example, we created a folder for our project to be Django_First where we created our virtual environment. Then running the Django start project command, we ended up with two folders with the same name.

NB: To specify the name of the configuration folder, we need to be sure that the folder exists already in that directory.

Best Practice

Given that we have created the folder that carries the name of our project, to effectively create a Django project without having all the nested folders, we can run the command

django-admin startproject config .

This will be our new structure

.
├── Django_First
│   ├── config
│   │   ├── __init__.py
│   │   ├── asgi.py
│   │   ├── settings.py
│   │   ├── urls.py
│   │   └── wsgi.py
│   └── manage.py

From this new structure, the initial name of our project is maintained, and the configuration is now config .

Requirements

When it’s time to install packages, most developers will search for a package on pypi and install the version they want, or they’ll just use pip install <package-name> . And the latest version will be installed. Others will have just a single requirements.txt file.

Best Practice

If you are careful with backward compatibility and, also aware of the different environments you are running your project in, then you’ll want to make sure everyone running your project is using the right version. Also, unnecessary packages should not be installed in the environments.

A requirements.txt file is important for complex projects so that, anyone can get your project, create their virtual environment, and install all packages with the right version from this file, with the command pip install -r requirements.txt .

Here, we will consider the different environments our project can run in. For example; development , stagging , production , etc. We don’t want a package like ipython , which is commonly used during development to be installed in production for example.

So, we will create the different environments with their specific requirement files and packages.

  • base.txt : This will contain packages that are common to all environments
  • dev.txt : This will contain packages used only in development
.
├── Django_First
│   ├── config
│   │   ├── __init__.py
│   │   ├── asgi.py
│   │   ├── settings.py
│   │   ├── urls.py
│   │   └── wsgi.py
│   ├── manage.py
│   └── requirements
│       ├── base.txt
│       └── dev.txt

Now that we have our structure, let’s look at the packages we could install for each environment.

# base.txt
Django~=5.0
django-environ==0.11.2
# dev.txt
-r base.txt
ipython==8.18.1
django-extensions==3.2.3

Here, we have

  • Django : which is the main framework,
  • django-environ : to read environment variables
  • ipython : for a powerful interactive Python shell
  • django_extensions : To auto-import necessary packages and modules from Django when we start a shell.

NB: Installing from dev.txtindirectly install from base.txt thanks to the line -r base.txt . Also, many other environments can be created like production, etc. Check Django Web Application to Railway to see how I used the production environment to host a Django project on railway.

Settings

By default, Django comes with a file setting.pythat contains settings for the Django project. This is okay for a very simple Django project.

Best Practice

Just like we did for the requirements above, we want to be sure we can handle many different environments. Plus having configurations for all the environments in one file can’t be scalable.

Here, we will create a folder called settings/then in it, create different Python modules based on the environments.

.
├── Django_First
│   ├── config
│   │   ├── __init__.py
│   │   ├── asgi.py
│   │   ├── settings
│   │   │   ├── base.py
│   │   │   └── dev.py
│   │   ├── urls.py
│   │   └── wsgi.py
│   ├── manage.py
│   └── requirements
│       ├── base.txt
│       └── dev.txt
  • base.py : This will contain configurations common to all environments
  • dev.py : This will contain the development configurations

base.py

from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent.parent.parent
DEFAULT_APP = [
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
]
CREATED_APP = []  # custom apps goes here
THIRD_PARTY_APP = []  # third party apps goes here
INSTALLED_APPS = [*DEFAULT_APP, *CREATED_APP, *THIRD_PARTY_APP]
MIDDLEWARE = [
    "django.middleware.security.SecurityMiddleware",
    "django.contrib.sessions.middleware.SessionMiddleware",
    "django.middleware.common.CommonMiddleware",
    "django.middleware.csrf.CsrfViewMiddleware",
    "django.contrib.auth.middleware.AuthenticationMiddleware",
    "django.contrib.messages.middleware.MessageMiddleware",
    "django.middleware.clickjacking.XFrameOptionsMiddleware",
]
ROOT_URLCONF = "config.urls"
TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "DIRS": [],
        "APP_DIRS": True,
        "OPTIONS": {
            "context_processors": [
                "django.template.context_processors.debug",
                "django.template.context_processors.request",
                "django.contrib.auth.context_processors.auth",
                "django.contrib.messages.context_processors.messages",
            ],
        },
    },
]
WSGI_APPLICATION = "config.wsgi.application"
AUTH_PASSWORD_VALIDATORS = [
    {
        "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
    },
    {
        "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
    },
    {
        "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
    },
    {
        "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
    },
]
LANGUAGE_CODE = "en-us"
TIME_ZONE = "UTC"
USE_I18N = True
USE_TZ = True
STATIC_URL = "static/"
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"

dev.py

import environ
from .base import *
env = environ.Env(DEBUG=(bool, True))
environ.Env.read_env(str(BASE_DIR / ".env"))
SECRET_KEY = env.str("SECRET_KEY")
DEBUG = env.bool("DEBUG")
ALLOWED_HOSTS = ["*"]
THIRD_PARTY_APP = [
    "django_extensions",
]  # third party apps goes here
INSTALLED_APPS = INSTALLED_APPS + THIRD_PARTY_APP
# If you want to use postgresql instead, then uncomment this and comment that of sqlite3 bellow.
# DATABASES = {
#     "default": {
#         "ENGINE": "django.db.backends.postgresql_psycopg2",
#         "NAME": env.str("DB_NAME"),
#         "USER": env.str("DB_USER"),
#         "PASSWORD": env.str("DB_PWD"),
#         "HOST": env.str("DB_HOST"),
#         "PORT": env.str("DB_PORT"),
#     }
# }
DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.sqlite3",
        "NAME": BASE_DIR / "db.sqlite3",
    }
}

Django Applications

Many developers create their apps in the working directory by running the command

python3 manage.py startapp <app-name>

NB: This turns to crowd the working directory.

Best Practice

Here we will create our Django applications in the apps/folder. This way, all applications are grouped and easy to find. Also, it makes the working directory less crowded.

To create a new app, we do the following;

  • Create the apps/directory
  • cd to the apps/directory and run the startapp management command
python3 ../manage.py startapp <app-name>

NB: After startapp , what follows should be the name of the application. It doesn’t support paths. That’s why we have to cd to the apps/ before running the command, while referring back to the manage.py with ../

Core App

The core application will serve the purpose of containing code that is common to two or more applications.

For example, it is common for models to share the same fields; created_at , modified_at . Rather than duplicating these in all the models, we can have them in a separate application and import them where needed.

apps/core/abstracts/models.py

# apps/core/abstracts/models.py
from django.db import models
class CreatedModifiedAbstract(models.Model):
    created_at = models.DateTimeField(auto_now_add=True)
    modified_at = models.DateTimeField(auto_now=True)
    class Meta:
        abstract = True

NB: The code above defines an abstract model that should be inherited into a model.

.
├── Django_First
│   ├── apps
│   │   ├── __init__.py
│   │   └── core
│   │       ├── __init__.py
│   │       ├── abstracts
│   │       │   ├── __init__.py
│   │       │   └── models.py
│   │       └── apps.py
│   ├── config
│   │   ├── __init__.py
│   │   ├── asgi.py
│   │   ├── settings
│   │   │   ├── base.py
│   │   │   └── dev.py
│   │   ├── urls.py
│   │   └── wsgi.py
│   ├── manage.py
│   └── requirements
│       ├── base.txt
│       └── dev.txt

Command shortcuts with Makefile

Now that we have a different structure and, we are able to run our Django project in different environments, we will have lengthy commands because we have to specify the environment we are running in.

To create shortcuts for our various commands, we can use a Makefile. Here are common commands we can create shortcuts for when running in the development environment;

Makefile

dev-start:
 python3 manage.py runserver --settings=config.settings.dev
dev-startapp:
 cd apps && python3 ../manage.py startapp $(app) --settings=config.settings.dev
dev-migrate:
 python3 manage.py migrate --settings=config.settings.dev
dev-makemigrations:
 python3 manage.py makemigrations --settings=config.settings.dev
dev-dbshell:
 python3 manage.py dbshell --settings=config.settings.dev
dev-shell:
 python3 manage.py shell --settings=config.settings.dev
dev-shell-plus:
 python3 manage.py shell_plus --settings=config.settings.dev
dev-install:
 pip install -r requirements/dev.txt
dev-test:
 python3 manage.py test --settings=config.settings.dev

To run a command from the Makefile, we just run make <command-name> where <command-name> is a shortcut name in the Makefile. For example, to start our Django server, we will run

make dev-start

Conclusion

The Django framework is a powerful tool for creating complex web applications. However, many developers struggle with their project’s structure, which slows down productivity.

This article presented a boilerplate that can be used to create a Django project. It is structured using best practices and makes it possible to scale any Django project.