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.