null

Runscript в Django

Что это, и зачем?

Среди функционала, предоставляемого Django, есть возможность запуска скриптов, взаимодействующих с контекстом django-приложения, через manage-команду runscript.

Такие скрипты обычно создаются для действий по типу выгрузки данных из БД, заполнения БД тестовым пуллом объектов и других задач, которые выполняются по требованию, причем не настолько часто, чтобы создавать элемент в веб-интерфейсе для их вызова.

Также стоит упомянуть возможность запуска таких скриптов в CI/CD pipeline, что удобно для автоматизации. 

Как запустить? 

Структура Django-проекта довольно строго определена, и при создании скрипта также небоходимо учитывать это: скрипты должны располагаться в директориях ./scripts  ваших Django-приложений (app).

Например, допустим, у вас есть приложения first_app и second_app:

django_project_dir/
├-first_app/
│ └-scripts/
│   ├-first_script.py
├-second_app/
│ └-scripts/
│   ├-second_script.py
├-manage.py
├-other_folder/
│ └-some_file.py

Тогда все скрипты, находящиеся в директориях first_app/scripts и second_app/scripts будут зарегистрированы и доступны к исполнению через интерфейс manage.py runscript:

python ../manage.py runscript first_script second_script 

У команды runscript есть несколько опций, например:

  • --continue-on-error позволяет продолжить выполнение цепочки скриптов внутри одного вызова runscript при ошибке внутри нее 

  • --no-traceback и --traceback отвечают за отображение цепочек вызовов при ошибках

  • --script-args используется для задания аргументов, которые будут переданы в скрипт. Аргументы должны быть разделены пробелом

Более подробно с опциями можно ознакомиться, выполнив команду 

 python ../manage.py help runscript

Пишем свой скрипт

Наконец, перейдем к созданию своего скрипта. На самом деле в этом нет ничего сложного: скрипт должен представлять из себя файл с функцией run, которая и будет вызвана. Например вот простейший скрипт для удаления всех объектов из таблицы БД:

# scripts/delete_all_questions.py

from polls.models import Question

def run():
    # Fetch all questions
    questions = Question.objects.all()
    # Delete questions
    questions.delete()

Также мы можем передать аргументы в скрипт, и использовать их там:

# scripts/delete_all_questions.py
from datetime import timedelta

from django.utils import timezone

from polls.models import Question

def run(*args):
    # Получаем все объекты
    questions = Question.objects.all()
    # В зависимости от аргумента выполняем либо не выполняем фильтрацию 
    if 'staleonly' in args:
        questions = questions.filter(pub_date__lt=timezone.now() - timedelta(days=100))
    # Удаляем объекты
    questions.delete()

Для запуска скрипта с аргументом (в примере выше это аргумент stateonly) вызов будет выглядеть так:

python manage.py runscript delete_all_questions --script-args staleonly

Стоит упомянуть, что для обозначения исключений при исполнении скриптов принято использовать встроенный класс CommandError.