This article explains how to write our own Django custom commands that might help us to test, upload data, or extract valuable information in a production environment from a Django project.
For newcomers, Django is a popular web framework designed and written in Python by experienced developers.
The main advantage of a custom command is that all Django machinery is loaded and ready to be used. That means you can import models, execute queries to the database using Django’s ORM, and interact with all your project’s resources.
TOPICS
Create a new Django App and Update Django to use it
Visualize the required directory structure inside the app
Code end execute a few custom commands:
cmd_time.py
- show the current timestampcmd_apps
- list all registered appscmd_models
- list all apps and associated modelscmd_showcfg
- list all CFG keys and values
The source code can be found on GitHub (MIT License)
Create a new Django App
This tutorial assumes that we have a working Django project and we will move forward and create a new app via startapp subcommand:
$ python manage.py startapp app_customcmd
Inside the new app directory, we need to create a directory structure as shown below:
< PROJECT ROOT > <-- project directory
|
|-- app_customcmd/ <-- app directory
| |-- management/
| | +-- __init__.py
| | +-- commands/
| | +-- __init__.py
| | +-- cmd_....py <-- module where all commands are saved
Once we have built and configured Django to use the new app, all commands defined in the commands directory are automatically discovered even without an application restart.
✅ Configure Django
To execute our custom commands we need to activate app_customcmd in the project configuration:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app_customcmd', # <-- NEW
]
✅ Custom Commands
This section presents custom commands that will access and report a few things related to a Django project internals - Let's go!
👉 Show Current Time
This simple custom command helps us to accommodate the concept. Here is the code, saved in the app_customcmd/management/commands
directory:
from django.core.management.base import BaseCommand
from django.utils import timezone
class Command(BaseCommand):
help = 'Displays current time'
def handle(self, *args, **kwargs):
time = timezone.now().strftime('%X')
self.stdout.write("It's %s" % time)
The actual executed code resides in the handle method and the execution should display something similar to this:
$ python manage.py cmd_time
It 09:18:08 <-- The output from our custom CMD
The most important aspect regarding the code is the usage of BaseCommand as a super-class for our definition.
👉 Show Registered Apps
This sample will iterate over all registered and active apps and print their names.
from django.core.management.base import BaseCommand
from django.apps import apps
class Command(BaseCommand):
help = 'Displays registered apps'
def handle(self, *args, **kwargs):
for app in apps.get_app_configs():
self.stdout.write(" APP -> %s" % app.verbose_name)
Script execution and output
$ python manage.py cmd_apps
APP -> Administration # Default Django App
APP -> Authentication and Authorization # Default Django App
APP -> Content Types # Default Django App
APP -> Sessions # Default Django App
APP -> Messages # Default Django App
APP -> Static Files # Default Django App
APP -> App_Customcmd # <-- Our NEW App
👉 Show models for each app
This command is quite similar to the previous one, but provide also the registered models for each app.
from django.core.management.base import BaseCommand
from django.apps import apps
class Command(BaseCommand):
help = 'Displays registered apps and models'
def handle(self, *args, **kwargs):
# Iterate over apps
for app in apps.get_app_configs():
self.stdout.write(" APP -> %s" % app.verbose_name)
# Iterate over models # <-- New Code
for model in app.get_models(): # <-- New Code
self.stdout.write("\t |- (model) -> %s" % model) # <-- New Code
The execution and output
$ python manage.py cmd_models
APP -> Administration
|- (model) -> <class 'django.contrib.admin.models.LogEntry'>
APP -> Authentication and Authorization
|- (model) -> <class 'django.contrib.auth.models.Permission'>
|- (model) -> <class 'django.contrib.auth.models.Group'>
|- (model) -> <class 'django.contrib.auth.models.User'>
APP -> Content Types
|- (model) -> <class 'django.contrib.contenttypes.models.ContentType'>
APP -> Sessions
|- (model) -> <class 'django.contrib.sessions.models.Session'>
APP -> Messages
APP -> Static Files
APP -> App_Forms
APP -> App_Pdf
APP -> App_Customcmd
APP -> App
|- (model) -> <class 'app.models.Book'>
👉 Print Config Variables
This custom command scans the settings object for proprieties and prints all values.
from django.core.management.base import BaseCommand
from django.utils import timezone
from django.conf import settings
class Command(BaseCommand):
help = 'Displays project config'
def handle(self, *args, **kwargs):
# type( settings ) => <class 'django.conf.LazySettings'>
# settings.__dict__
# Iterate over apps
for key in settings.__dict__.keys():
self.stdout.write(" Cfg Key: " + key + " -> %s" % settings.__dict__[ key ] )
The output and execution
(env) PS D:\work\repo-samples\django-learn-by-coding> python manage.py cmd_showcfg
Cfg Key: _wrapped -> <Settings "config.settings">
Cfg Key: INSTALLED_APPS -> ['django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'app_forms', 'app_pdf', 'app_customcmd', 'app']
Cfg Key: DEBUG -> True
Cfg Key: LOGGING_CONFIG -> logging.config.dictConfig
Cfg Key: LOGGING -> {}
Cfg Key: DEFAULT_EXCEPTION_REPORTER -> django.views.debug.ExceptionReporter
Cfg Key: FORCE_SCRIPT_NAME -> None
Cfg Key: DEFAULT_TABLESPACE ->
Cfg Key: DEFAULT_AUTO_FIELD -> django.db.models.BigAutoField
Cfg Key: ABSOLUTE_URL_OVERRIDES -> {}
Cfg Key: AUTH_USER_MODEL -> auth.User
Cfg Key: DATABASES -> {'default': {'ENGINE': 'django.db.backends.sqlite3', 'NAME': WindowsPath('D:/work/repo-samples/django-learn-by-coding/db.sqlite3'), 'ATOMIC_REQUESTS': False, 'AUTOCOMMIT': True, 'CONN_MAX_AGE': 0, 'OPTIONS': {}, 'TIME_ZONE': None, 'USER': '', 'PASSWORD': '', 'HOST': '', 'PORT': '', 'TEST': {'CHARSET': None, 'COLLATION': None, 'MIGRATE': True, 'MIRROR': None, 'NAME': None}}}
...
(truncated output)
✅ In Summary
Writing custom commands in our Django projects might help us extract stats, execute cron jobs, and check the health of a project that runs in production.
✅ Resources
👉 Access AppSeed and start your next project
👉 Deploy Projects on Aws, Azure, and Digital Ocean via DeployPRO
👉 Create an amazing landing page with Simpllo, an open-source site builder
👉 Django App Generator - A 2nd generation App Builder