Custom Management Commands

A custom management command is a Python class you add to your app so you can run your own task from the command line with python manage.py , just like Django's built-in commands.

Learn Custom Management Commands in our free Django course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick…

Part of the free Django course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

In this lesson you'll subclass BaseCommand , implement handle , declare command-line arguments with add_arguments , print coloured output with self.stdout.write and self.style , and schedule the command to run automatically with cron.

A command is a class named Command that subclasses BaseCommand and implements handle(self, *args, **options) — that method is the entry point Django calls. The file's name becomes the command name. The class also carries a help string shown by manage.py help .

To accept input, implement add_arguments(self, parser) . Django hands you a standard argparse parser; you register positional arguments and --optional flags on it. After parsing, the values land in the options dictionary your handle receives.

Inside handle you write output with self.stdout.write() rather than print , and you colourise it with self.style — SUCCESS (green), WARNING (yellow), and ERROR (red). Once the command works, you schedule it with cron because it is just a normal program.

An argument's type converts the raw text from the command line. Fill in the blank so "5" is parsed into the integer 5 .

The folder layout is wrong, or a missing __init__.py hides the package.

✅ Fix: put the file in app/management/commands/ with __init__.py in both folders.

You declared --dry-run but tried to read options["dry-run"] with a hyphen.

✅ Fix: argparse converts hyphens to underscores — read options["dry_run"] .

❌ Output is hard to test or doesn't show in logs

You used print() inside handle instead of the command's stream.

✅ Fix: use self.stdout.write(...) (and self.style ) so output is captured properly.

Build a command that deletes expired sessions. It takes a required --before cutoff and a --dry-run flag that reports what would be deleted without changing anything.

Lesson complete — you can automate your project!

You can subclass BaseCommand , implement handle , declare inputs with add_arguments , read them from options , print styled output with self.stdout and self.style , and schedule the command with cron.

🚀 Up next: DRF Authentication & Permissions — secure your API with tokens and permission classes.

Practice quiz

What class must a custom management command subclass?

  • BaseCommand
  • Command
  • AppConfig
  • ManagementCommand

Answer: BaseCommand. A command class subclasses BaseCommand from django.core.management.base.

Which method is the entry point Django calls to run a command?

  • run()
  • execute()
  • handle(self, *args, **options)
  • main()

Answer: handle(self, *args, **options). handle() is where the command's work happens.

Where must a custom command file live to be discovered?

  • app/commands/
  • app/management/commands/
  • app/scripts/
  • project root

Answer: app/management/commands/. Commands go in app/management/commands/, both with __init__.py.

What becomes the name you type after manage.py?

  • The class name
  • The help string
  • The file name
  • The app label

Answer: The file name. The command's file name becomes the command name.

Where do you declare command-line arguments?

  • add_arguments(self, parser)
  • handle()
  • __init__()
  • get_arguments()

Answer: add_arguments(self, parser). add_arguments registers positional and optional arguments on the parser.

A flag declared as --dry-run is read from options under which key?

  • dry-run
  • dryRun
  • dry_run
  • --dry-run

Answer: dry_run. argparse converts hyphens to underscores, so it is options['dry_run'].

How should a command print output instead of using print()?

  • sys.stdout.flush()
  • self.stdout.write()
  • logging.info()
  • return the text

Answer: self.stdout.write(). self.stdout.write() integrates with Django's output handling.

Which style helper colourises a success message green?

  • self.success()
  • self.style.OK()
  • self.style.GREEN()
  • self.style.SUCCESS()

Answer: self.style.SUCCESS(). self.style.SUCCESS() prints the message in green.

How do you typically schedule a management command to run nightly?

  • With the operating system's scheduler, like cron
  • With a Django setting
  • Inside settings.py
  • It runs automatically

Answer: With the operating system's scheduler, like cron. A command is a normal program, so cron schedules it.

What does the parser come from in add_arguments?

  • A custom Django parser
  • A standard argparse parser Django provides
  • A JSON config
  • The handle method

Answer: A standard argparse parser Django provides. Django hands you a standard argparse parser to register arguments on.