Skip to content

Creating Commands

Gustavo Silva edited this page Feb 10, 2023 · 1 revision

A common use-case with Surface is running something periodically or as part of some external action (e.g API call) or user action. For this, we use django-admin commands.

Folder structure should match the following:

yourapp/
    __init__.py
    models.py
    management/
        __init__.py
        commands/
            __init__.py
            yournewcommand.py
    ...

yournewcommand.py:

from typing import Any, Dict

from django.core.management.base import BaseCommand, CommandParser



class Command(BaseCommand):
    help = 'This command does pretty much nothing'

    def add_arguments(self, parser: CommandParser) -> None:
        parser.add_argument('-q', '--quiet', action='store_true', help='Do not send notifications')

    def handle(self, *args, **options: Dict[str, Any]) -> None:
        if options['quiet']:
            ...
        ...

If the command is meant to fetch some data periodically from e.g an external API, we usually name them resync_xyz.py

If the command is meant to do any checks, we usually name them check_abc.py.

In both cases, consider adding a pydoc comment to the handle method to explain any design choices, the flags it implements and in which context should the command be used. This is key for maintenance and understand potential errors.

This command can then be called in the following ways:

  • Shell: $ python manage.py yournewcommand
  • In-code synchronous call: call_command("yournewcommand") using from django.core.management import call_command
  • In-code asynchronous / non-blocking call: run_async("yournewcommand") using from dkron.utils import run_async

If there's a requirement to only ever have one instance of your command running, use the @locked decorator in the class using from database_locks import locked. This is sometimes the case for continuous running commands.