11.5. Manage Commands

Custom management commands are especially useful for running standalone scripts or for scripts that are periodically executed from the UNIX crontab or from Windows scheduled tasks control panel.

11.5.1. Location

Custom management commands are stored in the myproject/shop/management/commands/COMMAND_NAME.py file:

shop
├── __init__.py
├── admin.py
├── apps.py
├── management
│   └── commands
│       └── my_command.py
├── models.py
├── tests.py
└── views.py

11.5.2. Example

The file must contain a class named Command that extends BaseCommand class:

>>> 
... from django.core.management.base import BaseCommand
...
...
... class Command(BaseCommand):
...     help = 'Help message for the command'
...
...     def handle(self, *args, **options):
...         # do something

11.5.3. Arguments

>>> 
... from django.core.management.base import BaseCommand
...
...
... class Command(BaseCommand):
...     help = 'Help message for the command'
...
...     def add_arguments(self, parser):
...         parser.add_argument('--action', dest='action', help='Action to do')
...         parser.add_argument('--file', dest='file', help='Filename to parse')
...
...     def handle(self, *args, **options):
...         action = options['action']
...         file = options['file']
...         if action == 'parse':
...             # do something

11.5.4. Use Case - 1

File management/commands/importcsv.py:

>>> 
... import csv
... from django.core.management import BaseCommand
... from django.utils.translation import gettext_lazy as _
... from contact.models import Contact
...
...
... class Command(BaseCommand):
...     help = _('Import data from CSV file')
...
...     def add_arguments(self, parser):
...         parser.add_argument('--file', dest='file', help=_('File to import data from'))
...
...     def handle(self, *args, **options):
...         file = options['file']
...         with open(file) as f:
...             header = f.readline()
...             reader = csv.DictReader(f, fieldnames=['firstname', 'lastname'])
...             for line in reader:
...                 Contact.add(**line)

11.5.5. Run

$ python manage.py importcsv --file=/tmp/myfile.csv

11.5.6. Call

>>> from django.core import management
>>>
>>> management.call_command('importcsv', file='/tmp/myfile.csv')  

11.5.7. Use Case - 1

  • Cleaning data in database

from django.core.management.base import BaseCommand
from shop.models import Customer


class Command(BaseCommand):
    help = 'Clean all customers names in database'

    def handle(self, *args, **options):
        for c in Customer.objects.all():
            c.firstname = c.firstname.title()
            c.lastname = c.lastname.title()
            c.save()

11.5.8. Use Case - 1

  • Import data from JSON

  • Usage: python manage.py loadjson --file=/tmp/myfile.json

File /tmp/myfile.json:

[{"firstname": "Mark", "lastname": "Watney", "salary": 10000.00},
 {"firstname": "Melissa", "lastname": "Lewis", "salary": 10000.00},
 {"firstname": "Rick", "lastname": "Martinez", "salary": 10000.00}]
>>> 
... import json
... from django.core.management.base import BaseCommand
... from django.utils.translation import gettext_lazy as _
... from django.utils.module_loading import import_string
...
...
... class Command(BaseCommand):
...     help = _('Load data from JSON')
...
...     def add_arguments(self, parser):
...         parser.add_argument('--file', dest='file', help=_('Filename to load'))
...         parser.add_argument('--model', dest='model', help=_('Model to load data'))
...
...     def handle(self, *args, **options):
...         file = options['file']
...         model_name = options['model']
...         model = import_string(f'demo.models.{model_name}')
...         with open(file, mode='rt') as file:
...             rows = json.load(file)
...         result = [model(**row) for row in rows]
...         model.objects.bulk_create(result)
...
...         msg = self.style.SUCCESS(_('Success'))
...         self.stdout.write(msg)

11.5.9. Use Case - 2

  • Import data from CSV

  • Usage: python manage.py loadcsv --file=/tmp/myfile.csv

File /tmp/myfile.csv:

firstname,lastname,salary
Mark,Watney,10_000
Melissa,Lewis,20_000
Rick,Martinez,30_000
John,Doe,50_000
>>> 
... import csv
... from django.core.management.base import BaseCommand
... from django.utils.translation import gettext_lazy as _
... from demo.models import Person
...
...
... class Command(BaseCommand):
...     help = _('Load data from CSV')
...
...     def add_arguments(self, parser):
...         parser.add_argument('--file', dest='file', help=_('Filename to load'))
...
...     def handle(self, *args, **options):
...         file = options['file']
...         with open(file, mode='rt') as file:
...             reader = csv.DictReader(file)
...             rows = list(reader)
...         result = [Person(**row) for row in rows]
...         Person.objects.bulk_create(result)
...         self.stdout.write(self.style.SUCCESS(_('Success')))

11.5.10. Use Case - 3

  • Delete all data in specific app and Model

  • Usage: python manage.py purge --app=demo --model=Person

File myproject/core/management/commands/purge.py:

>>> 
... from django.core.management.base import BaseCommand
... from django.utils.module_loading import import_string
... from django.utils.translation import gettext_lazy as _
... from django.conf import settings
...
...
... class Command(BaseCommand):
...     help = _('Deletes all data in model')
...
...     def add_arguments(self, parser):
...         parser.add_argument('--app', dest='app', required=True, help=_('App to delete data from'))
...         parser.add_argument('--model', dest='model', required=True, help=_('Model to delete data from'))
...
...     def handle(self, *args, **options):
...         app_name = options['app']
...         model_name = options['model']
...
...         if not settings.DEBUG:
...             msg = _('Cannot run while not in DEBUG mode')
...             self.stdout.write(self.style.ERROR(msg))
...             exit(1)
...
...         msg = _(f'This command will DELETE ALL data in {app_name}.{model_name}')
...         self.stdout.write(self.style.WARNING(msg))
...
...         while True:
...             confirm = input('Do you want to continue? [yes/no]: ')
...             if confirm in ('yes', 'no'):
...                 break
...
...         if confirm == 'no':
...             msg = _('Data left untouched')
...             self.stdout.write(self.style.NOTICE(msg))
...             exit(1)
...
...         if confirm == 'yes':
...             model = import_string(f'{app_name}.models.{model_name}')
...
...             msg = _(f'Deleting all records in {app_name}.{model_name}')
...             self.stdout.write(self.style.NOTICE(msg))
...
...             model.objects.all().delete()
...
...             msg = _('All records deleted')
...             self.stdout.write(self.style.SUCCESS(msg))

11.5.11. Assignments

# FIXME: Write tests

# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author

# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`

# %% About
# - Name: Django Utils ManagementCommand
# - Difficulty: medium
# - Lines: 15
# - Minutes: 13

# %% English
# 0. Use `myproject.shop`
# 1. Create management command `shop/management/commands/i18n.py`
# 2. Program should scan all directories that have `locale` directory
# 3. In each such directory run command `python manage.py makemessages -l pl`
# 4. At the end run command `python manage.py compilemessages`

# %% Polish
# 0. Użyj `myproject.shop`
# 1. Stwórz komendę zarządzania `shop/management/commands/i18n.py`
# 2. Program powinien przeskanować wszystkie katalogi, które posiadają katalog `locale`
# 3. W każdym takim katalogu uruchom polecenie `python manage.py makemessages -l pl`
# 4. Na końcu uruchom polecenie `python manage.py compilemessages`

# %% Tests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 10), \
'Python 3.10+ required'
"""

...

# FIXME: Write tests

# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author

# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`

# %% About
# - Name: Django Utils ManagementCommand
# - Difficulty: medium
# - Lines: 15
# - Minutes: 13

# %% English
# 0. Use `myproject.shop`
# 1. Create management command `shop/management/commands/export_csv.py`
# 2. Program exports all `shop.Product` data to CSV format (use stdout)
# 3. For other communication use (stderr)

# %% Polish

# 0. Użyj `myproject.shop`
# 1. Stwórz komendę zarządzania `shop/management/commands/export_csv.py`
# 2. Program eksportuje wszystkie dane `shop.Product` do formatu CSV (użyj stdout)
# 3. Do pozostałej komunikacji użyj (stderr)

# %% Hints
# - `QuerySet.all()`
# - `QuerySet.values_list()`
# - `map(str, row)`
# - `','.join(data)`

# %% Tests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 10), \
'Python 3.10+ required'
"""

...

# FIXME: Write tests

# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author

# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`

# %% About
# - Name: Django Utils ManagementCommand
# - Difficulty: medium
# - Lines: 15
# - Minutes: 13

# %% English
# 0. Use `myproject.shop`
# 1. Create management command `shop/management/commands/export_json.py`
# 2. Program exports all `shop.Product` data to JSON format (use stdout)
# 3. For other communication use (stderr)

# %% Polish
# 0. Użyj `myproject.shop`
# 1. Stwórz komendę zarządzania `shop/management/commands/export_json.py`
# 2. Program eksportuje wszystkie dane `shop.Product` do formatu JSON (użyj stdout)
# 3. Do pozostałej komunikacji użyj (stderr)

# %% Hints
# - `QuerySet.all()`
# - `django.core.serializers.serialize`
# - `serialize('json', data, fields=..., indent=2)`

# %% Tests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 10), \
'Python 3.10+ required'
"""

...

# FIXME: Write tests

# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author

# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`

# %% About
# - Name: Django Utils ManagementCommand
# - Difficulty: medium
# - Lines: 15
# - Minutes: 13

# %% English
# 0. Use `myproject.shop`
# 1. Create management command `flower/management/commands/csv-import.py`
# 2. Program takes CSV file path as an argument
# 3. Program should read CSV file and import data to the database to `Flower` model

# %% Polish
# 0. Użyj `myproject.shop`
# 1. Stwórz komendę zarządzania `flower/management/commands/csv-import.py`
# 2. Program przyjmuje ścieżkę do pliku CSV jako argument
# 3. Program powinien odczytać plik CSV i zaimportować dane do bazy danych do modelu `Flower`

# %% Tests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 10), \
'Python 3.10+ required'
"""

...