Как создать и для чего используются Custom Management Commands в Django

Ответ

Custom Management Commands в Django — это способ расширить функциональность утилиты manage.py для автоматизации рутинных задач, связанных с вашим проектом.

Назначение

Они идеально подходят для:

  • Импорта/экспорта данных: например, загрузка данных из CSV-файла в базу данных.
  • Периодических задач: выполнение регулярных операций (например, очистка старых записей), которые можно запускать через cron.
  • Административных скриптов: выполнение сложных действий, которые неудобно делать через админ-панель или shell.

Как создать команду

  1. Внутри вашего Django-приложения создайте структуру каталогов: management/commands/.

    my_app/
    ├── __init__.py
    ├── models.py
    ├── management/
    │   ├── __init__.py
    │   └── commands/
    │       ├── __init__.py
    │       └── my_command.py
    └── ...
  2. В файле my_command.py создайте класс, унаследованный от BaseCommand.

Пример простой команды:

# my_app/management/commands/my_command.py

from django.core.management.base import BaseCommand, CommandError
from my_app.models import MyModel

class Command(BaseCommand):
    # Краткое описание команды, которое будет видно в `python manage.py help`
    help = 'Описание моей кастомной команды'

    def add_arguments(self, parser):
        # Добавление позиционных и именованных аргументов
        # Использует стандартный модуль Python `argparse`
        parser.add_argument('model_ids', nargs='+', type=int, help='ID моделей для обработки')
        parser.add_argument('--delete', action='store_true', help='Удалить объекты вместо обновления')

    def handle(self, *args, **options):
        # Основная логика команды
        model_ids = options['model_ids']
        should_delete = options['delete']

        for model_id in model_ids:
            try:
                model = MyModel.objects.get(pk=model_id)
                if should_delete:
                    model.delete()
                    self.stdout.write(self.style.SUCCESS(f'Объект {model_id} успешно удален.'))
                else:
                    # Какая-то логика обновления
                    self.stdout.write(f'Объект {model_id} обработан.')
            except MyModel.DoesNotExist:
                raise CommandError(f'Объект с ID "{model_id}" не найден.')

        self.stdout.write(self.style.SUCCESS('Команда успешно выполнена!'))

Запуск команды

Команда запускается из терминала так же, как и стандартные команды Django:

# Вызов команды с аргументами
python manage.py my_command 1 2 3

# Вызов с флагом
python manage.py my_command 1 2 --delete

Ответ 18+ 🔞

О, слушай, смотри, вот есть у тебя в Django эта штука — Custom Management Commands. Ну, это как бы расширение для твоей manage.py, чтобы автоматизировать всякую рутину, которую вручную делать — пиздец как заебало.

Зачем это вообще нужно, блядь?

Ну, например:

  • Тащить данные туда-сюда: загрузить кучу записей из CSV в базу — идеально.
  • Периодические дела: чистить старый хлам из базы, что-то обновлять — можно на cron повесить и забыть.
  • Админские скрипты: когда через админку делать неудобно, а в shell писать — нихуя неочевидно.

Как эту магию создать, ёпта?

  1. Внутри своего приложения наделай папок: management/commands/. Главное, не проеби __init__.py в каждой, а то Django нихуя не увидит.

    my_app/
    ├── __init__.py
    ├── models.py
    ├── management/
    │   ├── __init__.py
    │   └── commands/
    │       ├── __init__.py
    │       └── my_command.py  <-- вот тут наше всё
    └── ...
  2. В файле my_command.py пишешь класс, который наследуется от BaseCommand. Без этого — нихуя не работает.

Вот тебе пример, чтоб понятно было:

# my_app/management/commands/my_command.py

from django.core.management.base import BaseCommand, CommandError
from my_app.models import MyModel

class Command(BaseCommand):
    # Описание команды, которое видно в `python manage.py help`. Пиши нормально, а не как в голову взбредёт.
    help = 'Моя команда, которая делает что-то полезное, а не просто пиздит'

    def add_arguments(self, parser):
        # Тут настраиваешь аргументы, как в обычном argparse. Ничего нового, ебать.
        parser.add_argument('model_ids', nargs='+', type=int, help='IDшники моделей, которые будем мучать')
        parser.add_argument('--delete', action='store_true', help='Если передать — нихуя не обновим, а удалим нахуй')

    def handle(self, *args, **options):
        # А вот тут уже основная логика, ради чего всё и затевалось.
        model_ids = options['model_ids']
        should_delete = options['delete']

        for model_id in model_ids:
            try:
                model = MyModel.objects.get(pk=model_id)
                if should_delete:
                    model.delete()
                    self.stdout.write(self.style.SUCCESS(f'Объект {model_id} успешно отправился в пизду.'))
                else:
                    # Делаем что-то полезное, а не просто удаляем
                    self.stdout.write(f'Объект {model_id} обработан. Жив, здоров.')
            except MyModel.DoesNotExist:
                raise CommandError(f'Объект с ID "{model_id}" не найден, ты че, слепой?')

        self.stdout.write(self.style.SUCCESS('Всё, готово, расходимся!'))

Как запустить эту красоту?

Всё просто, как три копейки:

# Просто вызываем с аргументами
python manage.py my_command 1 2 3

# А можно и с флагом, чтоб удалить всё к хуям
python manage.py my_command 1 2 --delete

Вот и вся магия. Ничего сложного, зато удобно, как тёплый тапок. Главное — не забудь про __init__.py, а то будешь потом ебаться, почему не работает.