Django Typer

Use static typing to define the CLI for your Django management commands with Typer. Optionally use the provided TyperCommand class that inherits from BaseCommand. This class maps the Typer interface onto a class based interface that Django developers will be familiar with. All of the BaseCommand functionality is inherited, so that TyperCommand can be a drop in replacement.

django-typer makes it easy to:

Warning

Imports from django_typer have been deprecated and will be removed in 3.0! Imports have moved to django_typer.management:

# old way
from django_typer import TyperCommand, command, group, initialize, Typer

# new way!
from django_typer.management import TyperCommand, command, group, initialize, Typer

Installation

  1. 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]"
    
  2. Optionally add django_typer to your INSTALLED_APPS setting:

    INSTALLED_APPS = [
        ...
        'django_typer',
    ]
    

    You only need to install django_typer as an app if you want to use the shellcompletion 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.

Note

This documentation shows all examples using both the function oriented Typer-style interface and the class based Django-style interface in separate tabs. Each interface is functionally equivalent so the choice of which to use is a matter of preference and familiarity. All django-typer commands are instances of TyperCommand, including commands defined in the Typer-style interface. This means you may always specify a self argument to receive the instance of the command in your functions.

Basic Example

TyperCommand is a drop in extension to BaseCommand. All of the documented features of BaseCommand work the same way! Or, you may also use an interface identical to Typer’s. Simply import Typer from django_typer instead of typer.

management/commands/basic.py
1from django_typer.management import TyperCommand
2
3
4class Command(TyperCommand):
5    def handle(self, arg1: str, arg2: str, arg3: float = 0.5, arg4: int = 1):
6        """
7        A basic command that uses Typer
8        """
basic Usage: ./manage.py basic [OPTIONS] ARG1 ARG2 A basic command that uses Typer ╭─ Arguments ──────────────────────────────────────────────────────────────────╮ *arg1TEXT[default: None][required] *arg2TEXT[default: None][required] ╰──────────────────────────────────────────────────────────────────────────────╯ ╭─ Options ────────────────────────────────────────────────────────────────────╮ --arg3FLOAT  [default: 0.5] --arg4INTEGER[default: 1] --helpShow this message and exit. ╰──────────────────────────────────────────────────────────────────────────────╯ ╭─ Django ─────────────────────────────────────────────────────────────────────╮ --versionShow program's version number and exit. --settingsTEXTThe Python path to a settings module, e.g.        "myproject.settings.main". If this isn't          provided, the DJANGO_SETTINGS_MODULE environment  variable will be used.                            --pythonpathPATHA directory to add to the Python path, e.g.       "/home/djangoprojects/myproject".                 [default: None]                                   --tracebackRaise on CommandError exceptions --no-colorDon't colorize the command output. --force-colorForce colorization of the command output. --skip-checksSkip system checks. ╰──────────────────────────────────────────────────────────────────────────────╯

Multiple Subcommands Example

Commands with multiple subcommands can be defined:

management/commands/multi.py
 1import typing as t
 2
 3from django.utils.translation import gettext_lazy as _
 4from typer import Argument
 5
 6from django_typer.management import TyperCommand, command
 7
 8
 9class Command(TyperCommand):
10    """
11    A command that defines subcommands.
12    """
13
14    @command()
15    def create(
16        self,
17        name: t.Annotated[
18            str, Argument(help=_("The name of the object to create."))
19        ],
20    ):
21        """
22        Create an object.
23        """
24
25    @command()
26    def delete(
27        self,
28        id: t.Annotated[
29            int, Argument(help=_("The id of the object to delete."))
30        ],
31    ):
32        """
33        Delete an object.
34        """
multi Usage: ./manage.py multi [OPTIONS] COMMAND [ARGS]... A command that defines subcommands. ╭─ Options ────────────────────────────────────────────────────────────────────╮ --helpShow this message and exit. ╰──────────────────────────────────────────────────────────────────────────────╯ ╭─ Django ─────────────────────────────────────────────────────────────────────╮ --versionShow program's version number and exit. --settingsTEXTThe Python path to a settings module, e.g.        "myproject.settings.main". If this isn't          provided, the DJANGO_SETTINGS_MODULE environment  variable will be used.                            --pythonpathPATHA directory to add to the Python path, e.g.       "/home/djangoprojects/myproject".                 [default: None]                                   --tracebackRaise on CommandError exceptions --no-colorDon't colorize the command output. --force-colorForce colorization of the command output. --skip-checksSkip system checks. ╰──────────────────────────────────────────────────────────────────────────────╯ ╭─ Commands ───────────────────────────────────────────────────────────────────╮ create Create an object.                                                   delete Delete an object.                                                   ╰──────────────────────────────────────────────────────────────────────────────╯
create Usage: ./manage.py multi create [OPTIONS] NAME Create an object. ╭─ Arguments ──────────────────────────────────────────────────────────────────╮ *nameTEXTThe name of the object to create.[default: None] [required]                        ╰──────────────────────────────────────────────────────────────────────────────╯ ╭─ Options ────────────────────────────────────────────────────────────────────╮ --helpShow this message and exit. ╰──────────────────────────────────────────────────────────────────────────────╯
delete Usage: ./manage.py multi delete [OPTIONS] ID Delete an object. ╭─ Arguments ──────────────────────────────────────────────────────────────────╮ *idINTEGERThe id of the object to delete.[default: None] [required]                      ╰──────────────────────────────────────────────────────────────────────────────╯ ╭─ Options ────────────────────────────────────────────────────────────────────╮ --helpShow this message and exit. ╰──────────────────────────────────────────────────────────────────────────────╯

Grouping and Hierarchies Example

Or 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
4.76190
./manage.py hierarchy math multiply 10 2
20.00

Any number of groups and subcommands and subgroups of other groups can be defined allowing for arbitrarily complex command hierarchies. The Typer-style interface builds a TyperCommand class for us that allows you to optionally accept the self argument in your commands.

management/commands/hierarchy.py
 1import typing as t
 2from functools import reduce
 3
 4from django.utils.translation import gettext_lazy as _
 5from typer import Argument, Option
 6
 7from django_typer.management import TyperCommand, group
 8
 9
10class Command(TyperCommand):
11    help = _("A more complex command that defines a hierarchy of subcommands.")
12
13    precision = 2
14
15    @group(help=_("Do some math at the given precision."))
16    def math(
17        self,
18        precision: t.Annotated[
19            int, Option(help=_("The number of decimal places to output."))
20        ] = precision,
21    ):
22        self.precision = precision
23
24    @math.command(help=_("Multiply the given numbers."))
25    def multiply(
26        self,
27        numbers: t.Annotated[
28            t.List[float], Argument(help=_("The numbers to multiply"))
29        ],
30    ):
31        return f"{reduce(lambda x, y: x * y, [1, *numbers]):.{self.precision}f}"
32
33    @math.command()
34    def divide(
35        self,
36        numerator: t.Annotated[float, Argument(help=_("The numerator"))],
37        denominator: t.Annotated[float, Argument(help=_("The denominator"))],
38        floor: t.Annotated[bool, Option(help=_("Use floor division"))] = False,
39    ):
40        """
41        Divide the given numbers.
42        """
43        if floor:
44            return str(numerator // denominator)
45        return f"{numerator / denominator:.{self.precision}f}"
hierarchy Usage: ./manage.py hierarchy [OPTIONS] COMMAND [ARGS]... A more complex command that defines a hierarchy of subcommands. ╭─ Options ────────────────────────────────────────────────────────────────────╮ --helpShow this message and exit. ╰──────────────────────────────────────────────────────────────────────────────╯ ╭─ Django ─────────────────────────────────────────────────────────────────────╮ --versionShow program's version number and exit. --settingsTEXTThe Python path to a settings module, e.g.        "myproject.settings.main". If this isn't          provided, the DJANGO_SETTINGS_MODULE environment  variable will be used.                            --pythonpathPATHA directory to add to the Python path, e.g.       "/home/djangoprojects/myproject".                 [default: None]                                   --tracebackRaise on CommandError exceptions --no-colorDon't colorize the command output. --force-colorForce colorization of the command output. --skip-checksSkip system checks. ╰──────────────────────────────────────────────────────────────────────────────╯ ╭─ Commands ───────────────────────────────────────────────────────────────────╮ math Do some math at the given precision.                                  ╰──────────────────────────────────────────────────────────────────────────────╯
math Usage: ./manage.py hierarchy math [OPTIONS] COMMAND [ARGS]... Do some math at the given precision. ╭─ Options ────────────────────────────────────────────────────────────────────╮ --precisionINTEGERThe number of decimal places to output. [default: 2]                            --helpShow this message and exit. ╰──────────────────────────────────────────────────────────────────────────────╯ ╭─ Commands ───────────────────────────────────────────────────────────────────╮ multiply Multiply the given numbers.                                       divide   Divide the given numbers.                                         ╰──────────────────────────────────────────────────────────────────────────────╯
multiply Usage: ./manage.py hierarchy math multiply [OPTIONS] NUMBERS... Multiply the given numbers. ╭─ Arguments ──────────────────────────────────────────────────────────────────╮ *numbersNUMBERS...The numbers to multiply[default: None] [required]              ╰──────────────────────────────────────────────────────────────────────────────╯ ╭─ Options ────────────────────────────────────────────────────────────────────╮ --helpShow this message and exit. ╰──────────────────────────────────────────────────────────────────────────────╯
divide Usage: ./manage.py hierarchy math divide [OPTIONS] NUMERATOR DENOMINATOR Divide the given numbers. ╭─ Arguments ──────────────────────────────────────────────────────────────────╮ *numerator  FLOATThe numerator[default: None][required] *denominatorFLOATThe denominator[default: None][required] ╰──────────────────────────────────────────────────────────────────────────────╯ ╭─ Options ────────────────────────────────────────────────────────────────────╮ --floor--no-floorUse floor division[default: no-floor] --helpShow this message and exit. ╰──────────────────────────────────────────────────────────────────────────────╯

Contents: