Почему я получаю DoesNotExist для родительской модели в save_related
Я запускаю задачу celery в save_related
, которая принимает идентификатор родительского объекта и выполняет запрос get для получения экземпляра для использования.
celery получает form.instance.id
как parent_id
ParentModel.objects.get(id=parent_id)
Проблема в том, что некоторые запросы get завершаются с ошибкой DoesNotExist
. Это не имеет смысла, потому что в документации Django ясно сказано, что "Обратите внимание, что в этот момент родительский объект и его форма уже сохранены.". Также почему не происходит сбоя у других?
Я собираюсь поэкспериментировать с тем, что произойдет, если я вызову super().save_related(...)
перед задачей celery, но я не думаю, что это должно быть проблемой из-за примечания выше.
Django==4.2.3
Скорее всего, проблема возникает из-за того, что задачи celery выполняются асинхронно после возврата save_related.
Когда save_related вызывает super().save_related(), он сохраняет все данные модели в базе данных. Но затем задачи celery выполняются асинхронно.
К моменту запуска задач celery транзакции базы данных из save_related уже были зафиксированы. Поэтому любые новые идентификаторы первичных ключей, созданные во время сохранения, еще не видны в задачах celery.
Чтобы исправить это, нужно передавать задачам celery не просто ID, а реальный экземпляр модели. Таким образом, они будут иметь актуальный экземпляр с правильным ID, а не будут вынуждены получать его заново.
def save_related(self, request, form, formsets, change):
instance = form.instance
# save everything
super().save_related(request, form, formsets, change)
# pass instance rather than just ID
jobs = group(
bulk_update_task.s(instance, *args)
for ids_chunk in chunks(ids, 128)
)
jobs()
@celery_app.task
def bulk_update_task(instance, var1, var2, var3):
# use instance rather than refetching
parent = instance
# rest of task
Передавая реальный объект экземпляра, задачи celery будут иметь доступ к актуальному первичному ключу без необходимости повторной выборки из базы данных.