7 Days Challenge to Push Your Hard skill Using Django Part 2: Build Better Project Structure and Understand Golden Rule In Django

Feri Lukmansyah
7 min readMay 20, 2021
image from unsplash

hello welcome to another blog post, in this post I will talk about structuring the Django project so that it is easy for developers to understand and manage and can easily include various features, this is the second day of the challenge “7 Days Push Your Hard Skill Using Django” let’s see how the tip is

Understanding The Golden Rule of Django Apps Design

when creating a Django project, it is required that each of those applications should focus on the task, each application must focus on the task, otherwise, the application will be difficult to describe and the size will be enormous, and will be difficult to manage, therefore the application must be broken down into several parts.

Practical Example Apps in Django Project

I will show a comparison between 2 project structures, namely the default and the one that is made modularly based on the function of each application

Uncommon Apps Module

to build a better project structure we must separate them based on a function and where it will be used, whether in production or locally, in development condition or ready for production, Let's look default Django Project when we created with django-admin startproject <project_name> shown bellow

mysite/
manage.py
mysite/
__init__.py
settings.py
urls.py
asgi.py
wsgi.py

and here if you create apps

.
├── manage.py
├── mysite
│ ├── asgi.py
│ ├── __init__.py
│ ├── __pycache__
│ │ ├── __init__.cpython-39.pyc
│ │ └── settings.cpython-39.pyc
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
└── polls
├── admin.py
├── apps.py
├── __init__.py
├── migrations
│ └── __init__.py
├── models.py
├── tests.py
└── views.py

with default structure, we can manage one or two apps in Django but how to manage 3 or more Django apps if an inside app has multiple services or function, definitely going to be difficult to manage and manage, because it’s confusing 😩, now I will share tips Best Practice Build Better Structure in Django Projects.

Here are some tips on why we need to change the default project structure in Django

  • Naming Django Apps
  • Keep Django Apps Small

How to Naming Django Apps

other tips A very important little tip is how to name Django Apps, if possible keep single-word names like blog , admin etc

Keep your Django Apps Small

like a UNIX philosophy, “each app should be tightly focused on its task” mean is keep your apps, program, or system modular and focused in one task, because if apps have a multiple service or task, it will cause confusion if our Django project becomes big, therefore solving apps by task is very important.

Common Apps Module

here is a Common Apps Module

.
├── apps
│ ├── api
│ │ ├── admin.py
│ │ ├── apps.py
│ │ ├── __init__.py
│ │ ├── migrations
│ │ │ └── __init__.py
│ │ ├── models.py
│ │ ├── tests.py
│ │ └── views.py
│ ├── __init__.py
│ └── store
│ ├── admin.py
│ ├── apps.py
│ ├── __init__.py
│ ├── migrations
│ │ └── __init__.py
│ ├── models.py
│ ├── tests.py
│ └── views.py
├── config
│ ├── asgi.py
│ ├── __init__.py
│ ├── settings
│ │ ├── base.py
│ │ ├── __init__.py
│ │ └── local.py
│ ├── urls.py
│ └── wsgi.py
├── db.sqlite3
├── manage.py
├── README.md
└── requirements
├── base.txt
└── local.txt

in that structure, we separate apps in a specific directory, in this case, we have two apps called blog and store they separated in a specific directory called apps it makes the application very easy to manage and easily added with new features for the system may be modular and easier to import the required modules 😄

Using Multiple Settings Files In Django

By default Django, there is only one settings.py file, but we can use more than one settings file in Django, therefore we can make it even more modular, this is necessary because when we deploy a Django project to production we need to make sure that the configuration is It’s true that it’s ready on a production server, but what if we need to develop an application that we have deployed again, what if a new developer is involved in developing the application, how can our application be easily developed and understood by other developers?

Of course, the question is very important, why? because we don’t want to rewrite the settings.py file if it’s already in production, other than that what if each developer rewrites the settings.py file differently, it would be very confusing and time-consuming, so we can make settings that are more modular. and in accordance with the task that has been determined.

this is the same as the django framework’s philosophy, which is Don’t repeat yourself

okay, let's take an example to create a module named settings like this

.
├── base.py
├── __init__.py
└── local.py

in a settings , the module has a 2 file base.py and local.py

  • base.py Base Settings Configuration
  • local.py Inheritance from base.py but Configured in local development Only

now let's configure base.py in this file, we create base settings configs like TEMPLATES, STATIC_DIRS, MEDIA_ROOT, INSTALLED_APPS, MIDDLEWARE, SECURITY, DATABASE, and many others

# settings/base.py"""
Django settings for config project.

Generated by 'django-admin startproject' using Django 3.2.3.

For more information on this file, see
https://docs.djangoproject.com/en/3.2/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/3.2/ref/settings/
"""

from pathlib import Path
from os.path import abspath, dirname, join
import environ
# Build paths inside the project like this: BASE_DIR / 'subdir'.

# BASE DIR PATH SETTINGS


# Handling Mapping Path Using Function

"""
using this function if need to mapping basedirs or test path
"""

def root(*dirs):
base_dir = join(dirname(__file__), '..', '..')
return abspath(join(base_dir, *dirs))

# OLD CONFIG

# BASE_DIR = environ.Path(__file__) - 3
# APPS_DIR = BASE_DIR.path("apps")
# CONFIG_DIRS = BASE_DIR.path("config")

# NEW CONFIG
BASE_DIR = Path(__file__).resolve().parent.parent.parent
APPS_DIR = BASE_DIR / 'apps'
CONFIG_DIRS = BASE_DIR / 'config'

# STATIC AND MEDIA CONFIG
MEDIA_ROOT = BASE_DIR / 'media'
STATIC_ROOT = BASE_DIR / 'static_root'


# LOAD ENVIRONTMENT
env = environ.Env()
env.read_env('.env')


# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]

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',
]

# Config URL
ROOT_URLCONF = 'config.urls'

TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR / 'templates'],
'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'


# DJANGO CORE
DEBUG = env.bool("DJANGO_DEBUG", False)

# Database
# https://docs.djangoproject.com/en/3.2/ref/settings/#databases

DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}


# Password validation
# https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators

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',
},
]


# Internationalization
# https://docs.djangoproject.com/en/3.2/topics/i18n/

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_L10N = True

USE_TZ = True


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.2/howto/static-files/

STATIC_URL = '/static/'

# Default primary key field type
# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

now let's create settings for local development, in this example local.py like this

"""
local development setings
"""

from .base import *

# DEBUG SETTINGS
DEBUG = True

# SECRET_KEY CONFIG
SECRET_KEY = env('DJANGO_SECRET_KEY')

now to use is this settings modules change default settings file in manage.py

# project_root/manage.pydef main():
"""Run administrative tasks."""
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings.local')
...

How To Register New Apps

finally, you create all Django apps in a specific directory if you need to register apps in settings, register apps in settings/base.py file and register inside INSTALLED_APPS like this

# settings/base.py
INSTALLED_APPS = (
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"django.contrib.sites",

# your apps <-
"apps.api",
"apps.store"
)

Break Down Requirements file

Try to imagine what if the needs in a production server and in development using the package version and using some additional modules or there are development dependencies.

well for that we can break down some modules that will be installed and used according to the stages, for example, we use the coverage module for testing purposes but in production we no longer need it, to solve this we can break the requirements file, let's create two requirements called base.txt and local.txt

# base.txt
Django==3.2.3
psycopg2-binary
djangorestframework
django-environ

in local.txt extend base.txt like this

-r base.txt # include base.txt
coverage==5.5

now let's look a requirements folder

.
├── base.txt
└── local.txt

now install with include folder path like this

pip install -r requirements/local.txt

after install run manage.py

python manage.py runserver

and open http://localhost:8000/ in the browser

localhost response with modular project structure

Conclusion

a project structure like this can make it easier to develop a site using Django and is easy to manage because the structure is modular and easy to understand and has standards, in the second days of the challenge I have learned “how to manage your project” 😄

References and Inspirations

--

--