Use Typer to define the CLI for your Django management commands. Provides a TyperCommand class that inherits from BaseCommand and allows typer-style annotated parameter types. All of the BaseCommand functionality is preserved, so that TyperCommand can be a drop-in replacement.
django-typer makes it easy to:
- Define your command CLI interface in a clear, DRY, and safe way using type hints.
- Create subcommand and group command hierarchies.
- Use the full power of Typer's parameter types to validate and parse command line inputs.
- Create beautiful and information dense help outputs.
- Configure the rendering of exception stack traces using rich.
- Install shell tab-completion support for TyperCommands and normal Django commands for bash, zsh, fish, and powershell.
- Create custom and portable shell tab-completions for your CLI parameters.
- Refactor existing management commands into TyperCommands because TyperCommand is interface compatible with BaseCommand.
Please refer to the full documentation for more information.
-
Clone django-typer from GitHub or install a release off PyPI:
pip install django-typer
rich is a powerful library for rich text and beautiful formatting in the terminal. It is not required but highly recommended for the best experience:
pip install "django-typer[rich]"
-
Add
django_typer
to yourINSTALLED_APPS
setting:INSTALLED_APPS = [ ... 'django_typer', ]
You only need to install django_typer as an app if you want to use the shell completion command to enable tab-completion or if you would like django-typer to install rich traceback rendering for you - which it does by default if rich is also installed.
For example, TyperCommands can be a very simple drop-in replacement for BaseCommands. All of the documented features of BaseCommand work!
from django_typer import TyperCommand
class Command(TyperCommand):
def handle(self, arg1: str, arg2: str, arg3: float = 0.5, arg4: int = 1):
"""
A basic command that uses Typer
"""
Commands with multiple subcommands can be defined:
import typing as t
from django.utils.translation import gettext_lazy as _
from typer import Argument
from django_typer import TyperCommand, command
class Command(TyperCommand):
"""
A command that defines subcommands.
"""
@command()
def create(
self,
name: t.Annotated[str, Argument(help=_("The name of the object to create."))],
):
"""
Create an object.
"""
@command()
def delete(
self, id: t.Annotated[int, Argument(help=_("The id of the object to delete."))]
):
"""
Delete an object.
"""
More complex groups and subcommand hierarchies can be defined. For example, this command defines a group of commands called math, with subcommands divide and multiply. The group has a common initializer that optionally sets a float precision value. We would invoke this command like so:
./manage.py hierarchy math --precision 5 divide 10 2.1
./manage.py hierarchy math multiply 10 2
import typing as t
from functools import reduce
from django.utils.translation import gettext_lazy as _
from typer import Argument, Option
from django_typer import TyperCommand, group
class Command(TyperCommand):
help = _("A more complex command that defines a hierarchy of subcommands.")
precision = 2
@group(help=_("Do some math at the given precision."))
def math(
self,
precision: t.Annotated[
int, Option(help=_("The number of decimal places to output."))
] = precision,
):
self.precision = precision
@math.command(help=_("Multiply the given numbers."))
def multiply(
self,
numbers: t.Annotated[
t.List[float], Argument(help=_("The numbers to multiply"))
],
):
return f"{reduce(lambda x, y: x * y, [1, *numbers]):.{self.precision}f}"
@math.command()
def divide(
self,
numerator: t.Annotated[float, Argument(help=_("The numerator"))],
denominator: t.Annotated[float, Argument(help=_("The denominator"))],
floor: t.Annotated[bool, Option(help=_("Use floor division"))] = False,
):
"""
Divide the given numbers.
"""
if floor:
return str(numerator // denominator)
return f"{numerator / denominator:.{self.precision}f}"