Задать вопрос
  • Django. Как отловить изменение в поле модели?

    werevolff
    @werevolff
    Здесь есть два популярных решения проблемы. Но, сперва, разберёмся в архитектуре.

    1. У нас работает некий таймер, который с некоторой периодичностью запускает python функцию.
    2. Эта функция фильтрует Card по статусу активности. Например:
    qs = Card.objects.filter(
        expired_data__gt = datetime.now()
    )


    3. Для инстансов в Queryset применяются выбранные действия.

    for instance in qs:
        do_something(instance)


    Итак, наша задача - найти этот таймер. Наиболее популярное решение - celery beat с periodic tasks

    Как это выглядит:

    1. Ставим на проект Celery, убедимся, что task.delay() работает для тестового таска.
    2. Создаём my_package/tasks.py.
    3. Создадим внутри таск, который фильтрует наши неактивные карточки и выполняет на них операцию с выставлением счёта, или что там у тебя.
    4. Заносим этот таск в settings.CELERY_BEAT_SCHEDULE

    CELERY_BEAT_SCHEDULE = {
        'my_task_name': {
            'task': 'my_package.tasks.my_task_name',
            'schedule': 24 * 60 * 60,  # 24 hours
            'args': ()
        }
    }


    5. Убедимся, что celery запущена в режиме beat.

    Что если мы не хотим ставить Celery? Есть решение в виде django-crontab.

    1. Ставим django-crontab.
    2. Создаём функцию, которая фильтрует и обрабатывает не активные карточки.
    3. Заносим нашу функцию в settings.CRONJOBS

    CRONJOBS = [
        ('*/5 * * * *', 'myapp.cron.my_scheduled_job')
    ]


    4. Выполняем python manage.py crontab add

    Дополнительно нам может потребоваться сохранять данные о том, какие карты игнорировать. Это можно решить элементарным добавлением поля ignore в модель и изменением правила фильтрации карточки.

    Для тестирования: берём таск или функцию для крона и выполняем её в тестах. Убедимся, что операция выполнена только для не активных карточек. Не забываем использовать CELERY_ALWAYS_EAGER=True в override_settings для теста.
    Ответ написан
    Комментировать