Здесь есть два популярных решения проблемы. Но, сперва, разберёмся в архитектуре.
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 для теста.