Django model pre_save update() имеет проблемы
Фон:
У меня есть модели Tag, у которых есть атрибут is_obsolete (по умолчанию установлен в False)
class Tag(model.Model):
...
is_obsolete = False
У меня есть модели StatusInfo, которые имеют внешний ключ к Tag через "affected_tag".
class StatusInfo(models.Model):
...
affected_tag = ForeignKey(Tag)
Когда создается экземпляр StatusInfo, он подает сигнал pre_save()
для изменения <StatusInfo>.affected_tag.is_obsolete=True
@receiver(pre_save, sender=StatusInfo)
def status_change_modification(sender, instance, *args, **kwargs):
"""
Changes tags and concepts to be obsolete or deprecated.
if a tag is obsolete or deprecated, all of the tag's concepts are obsolete or deprecated
"""
assert hasattr(instance.affected_tag, "id")
# set up variables
kwargs = {}
if instance.status == "d":
kwargs["is_deprecated"] = True
elif instance.status == "o":
kwargs["is_obsolete"] = True
inner_tag_status_change(instance, **kwargs)
def inner_tag_status_change(instance, **kwargs):
"""
update the is_updated fields for status_info
kwargs is either:
kwargs = {'is_obsolete': True} # or {'is_deprecated': True}
"""
affected = Tag.objects.filter(id=instance.affected_tag.id).first()
affected.__dict__.update(**kwargs)
affected.save(update_fields=list(kwargs.keys()))
Проблема
Я не могу понять, почему, когда это попадает в нижнюю часть теста, у меня все еще есть мой экземпляр Tag self.soon_to_be_obsolete_tag.is_obsolete == False
Я пробовал .update(**kwargs)
и .save(updated_fields=list(kwargs.keys()))
внутри inner_tag_status_change(), но изменения не сохраняются.
В режиме отладки все выглядит хорошо, но потом все возвращается назад на последней строке теста. Я работаю над отдельной копией или кэширование - вещь, которую я должен проверить?
в tests/test_status_model.py
def test_status_set_tag_info_obsolete(self):
""" check to see if the creation of a statusInfo
object which runs a pre_save signal changes
the soon_to_be_obsolete_tag to is_obsolete=True
"""
self.assertFalse(self.soon_to_be_obsolete_tag.is_obsolete)
self.main_status_info = StatusInfoFactory(
affected_tag=self.soon_to_be_obsolete_tag,
status="o", # obsolete
by=self.new_replacement_tag,
)
self.assertTrue(self.soon_to_be_obsolete_tag.is_obsolete) # still False
Основываясь на вашем тесте и логике предварительного сохранения, похоже, что вы обновляете тег в базе данных, но кэш Django для объекта тега, присоединенного к вашей модели, не обновляется.
Полагаю, проблема в том, что в обоих случаях status_change_modification
и inner_tag_status_change
вы обращались к instance.affected_tag
, но не обновляли instance.affected_tag
напрямую. Вместо этого вы взяли этот тег из базы данных с помощью дополнительного запроса и затем сохранили его.
Я предполагаю, что если вы обновите свой тест до этого, он будет работать:
self.assertTrue(Tag.objects.get(id=self.soon_to_be_obsolete_tag).is_obsolete)
Или если вы использовали refresh_from_db
для принудительного обновления self.soon_to_be_obsolete_tag
, это тоже должно работать:
self.soon_to_be_obsolete_tag.refresh_from_db()
self.assertTrue(self.soon_to_be_obsolete_tag.is_obsolete)
Вы также можете рассмотреть возможность обновления логики inner_tag_status_change
, чтобы обновлять тег напрямую (я не уверен, что это устранит проблему, с которой вы столкнулись, но это уберет дополнительный запрос SELECT
, который может быть не нужен):
def inner_tag_status_change(instance, **kwargs):
instance.affected_tag.__dict__.update(**kwargs)
instance.affected_tag.save(update_fields=list(kwargs.keys()))