Writing Custom Django Management Commands

Any command given with manage.py is called management command. Django comes with many built-in management commands like runserver, startapp etc. To see a full list of built-in management commands, type:
python manage.py help

The benefit of management command script is that this script executes within Django environment. You can run all of Django ORM queries within this script. You can import models. You have access to project resources.

In this tutorial we will see how to populate a table with some contents of a csv file.

First lets look at the file-structure:
First create a folder called management. Create a __init__.py file inside it. Then create a folder called commands inside management and create a __init__.py inside it.

*tip: It is always useful to either put management folder in an existing app or create a new app within your django project just for management commands. Keeping management inside an app (which is registered in INSTALLED_APPS in settings.py) makes it easy for the command to be located.

In my Django project, I created an app called my_app (python manage.py startapp my_app). I have put my management folder inside it. Then I create a file called test_command.py inside commands folder. The structure looks like so:

The management command gets the name of the file. So since the filename is test_command.py, the management command will be called test_command. We will be launching this command like so:
python manage.py test_command

Now lets write the most basic custom management command.

All management commands are subclasses of BaseCommand. The file always has a class called Command which is a subclass of BaseCommand. And this class always has a method
def handle(self, *args, **options):

Lets execute the management command.
python manage.py test_command

You will see ‘hello world’ printed. Note that inside management commands, we use stdout for printing to console.

Arguments

We can pass arguments with management commands. There are three kinds of arguments.

  1. Positional arguments: required. command will not run if these not passed
  2. optional arguments: optional. command will run even if these not passed
  3. Flag Arguments optional boolean

Arguments are parsed using Python’s argparse library.
For simple use cases, we will be able to use add_arguments convenience method in Django. For more customized use cases we will need to work with argparse library.

For handling arguments, we need to add a method called add_arguments.

Mandatory Arguments (Also called Positional Arguments)

We will create a management command called populate_currencies.py. This command will read a csv file with currency codes and fill a database table called Currencies. We will pass a mandatory argument called filename that will be the name of csv file we want to read.

since it is positional, we can use it like so:
python manage.py populate_currencies currencies.csv

See below for currencies.csv file

First, I’ll create a model to hold the currency codes. Add this to models.py file:

Then, I’ll paste a simple csv file here for your reference:

Put this csv file in the same folder as the management command populate_currencies.py. This is not a requirement but to keep matters simple, thats where we keep the csv file.

OK, so now populate_currencies.py for reading this csv and populating Currencies table is:

In this file, here is where we add the mandatory argument:

def add_arguments(self, parser):
parser.add_argument('filename', type=str, help='filename for csv file')

Also note that we can set Styles for console outputs like so:
self.style.SUCCESS(f'{line_count} entries added to Currencies')

Please check Epilogue for an entire list of styles.

We launch the command like so:
python manage.py populate_currencies currencies.csv

Due to its positional nature, filename argument is set to currencies.csv

Optional Arguments

The optional (and named) arguments can be passed in any order. In the example below you will find the definition of an argument named “prefix”, which will be used to compose the username field:

management/commands/create_users.py

Usage
python manage.py create_users 10 --prefix custom_user

or

python manage.py create_users 10 -p custom_user

If the prefix is used, the username field will be created as custom_user_xCVGn3yt56h. If not prefix, it will be created simply as xCVGn3yt56h – a random string.

Flag Arguments

Another type of optional arguments are flags, which are used to handle boolean values. Let’s say we want to add an --admin flag, to instruct our command to create a super user or to create a regular user if the flag is not present.

management/commands/create_users.py

Note:action='store_true' indicates, default value of true. This is straight from argparser (Python library). See here for details.

Usage:

python manage.py create_users 2 --admin
Or

python manage.py create_users 2 -a

Management command automation

Django management commands are typically run from the command line, requiring human intervention. However, there can be times when it’s helpful or necessary to automate the execution of management commands from other locations (e.g. a Django view method or shell).

For example, if a user uploads an image in a Django application and you want the image to become publicly accessible, you’ll need to run the collectstatic command so the image makes its way to the public consolidation location (STATIC_ROOT) . Similarly, you may want to run a cleanuprofile command every time a user logs in.

To automate the execution of management commands Django offers the django.core.management.call_command() method. Below illustrates the various ways in which you can use the call_command() method.

Django management automation with call_command()

The first option executes a management command without any arguments. The second option uses the interactive=False argument to indicate the command must not pause for user input (e.g. collectstatic always asks if you’re sure if you want to overwrite pre-existing files, the interactive=False argument avoids this pause and need for input).

The third option invokes the management command by first importing it and then invoking its Command() class directly vs. using the command string value. And finally, the fourth option — just like the third — in listing 5-35, uses a positional argument — declared as a standalone value (e.g. ‘stores’, 1) and a named argument — declared as a key=value (e.g. verbosity=0, delete=True).

Epilogue

A management command to display an entire list of styles is like so:

credit: I got the above code sample from here.




No Comments


You can leave the first : )



Leave a Reply

Your email address will not be published. Required fields are marked *