通过覆盖保存来规避 django-simple-history 的 F 表达式问题
Circumventing F expression problem of django-simple-history by overriding save
Django-simple-history 在每次保存目标模型时插入新记录。在 docs 中描述了 F 表达式的问题。我试图用重写的保存方法来规避这个问题。
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
# some other actions
self.refresh_from_db()
但似乎这不起作用。基础模型的post_save
信号是在super().save()
调用之后直接调用的吗?如果是这样,有没有办法解决这个问题,在目标模型更新中保持 F 表达式?
更新:保存的实例有一个使用 F 表达式定义的属性,因此在其他模块中调用此代码:
instance.some_attribute = (F('some_attribute') + 15)
instance.save(update_fields=['some_attribute'])
当 django-simple-history 的 post_save
信号尝试将 instance
的扩展副本插入到历史记录 table 时,它会引发错误。我试图在覆盖 save
方法中刷新实例以摆脱 some_attribute
中的 F 表达式,以便加载实际值。从回溯看来 post_save
是在 super().save()
调用之后,刷新之前调用的。这是 Django post_save 覆盖保存的方式吗?如果是,有没有办法不改更新码(保留F表达式的更新),解决模型存档中的历史插入问题?
django-simple-history
提供创建历史记录前后的信号:https://django-simple-history.readthedocs.io/en/2.7.0/signals.html
我建议在将实例保存到历史 table 之前使用它们来更新实例。这样的事情应该有效:
from django.dispatch import receiver
from simple_history.signals import (
pre_create_historical_record,
post_create_historical_record
)
@receiver(pre_create_historical_record)
def pre_create_historical_record_callback(sender, **kwargs):
instance = kwargs["instance"]
history_instance = kwargs["history_instance"]
if isinstance(instance, ModelYouWantToRefresh)
instance.refresh_from_db()
history_instance.some_attribute = instance.some_attribute
根据 Ross Mechanic 的回答我做了一个通用的解决方案
@receiver(
pre_create_historical_record,
dispatch_uid='simple_history_refresh')
def remove_f_expressions(sender, instance, history_instance, **kwargs):
f_fields = []
for field in history_instance._meta.fields:
if isinstance(getattr(history_instance, field.name), BaseExpression):
f_fields.append(field.name)
if f_fields:
instance.refresh_from_db()
for fld_name in f_fields:
setattr(history_instance, fld_name, getattr(instance, fld_name))
Django-simple-history 在每次保存目标模型时插入新记录。在 docs 中描述了 F 表达式的问题。我试图用重写的保存方法来规避这个问题。
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
# some other actions
self.refresh_from_db()
但似乎这不起作用。基础模型的post_save
信号是在super().save()
调用之后直接调用的吗?如果是这样,有没有办法解决这个问题,在目标模型更新中保持 F 表达式?
更新:保存的实例有一个使用 F 表达式定义的属性,因此在其他模块中调用此代码:
instance.some_attribute = (F('some_attribute') + 15)
instance.save(update_fields=['some_attribute'])
当 django-simple-history 的 post_save
信号尝试将 instance
的扩展副本插入到历史记录 table 时,它会引发错误。我试图在覆盖 save
方法中刷新实例以摆脱 some_attribute
中的 F 表达式,以便加载实际值。从回溯看来 post_save
是在 super().save()
调用之后,刷新之前调用的。这是 Django post_save 覆盖保存的方式吗?如果是,有没有办法不改更新码(保留F表达式的更新),解决模型存档中的历史插入问题?
django-simple-history
提供创建历史记录前后的信号:https://django-simple-history.readthedocs.io/en/2.7.0/signals.html
我建议在将实例保存到历史 table 之前使用它们来更新实例。这样的事情应该有效:
from django.dispatch import receiver
from simple_history.signals import (
pre_create_historical_record,
post_create_historical_record
)
@receiver(pre_create_historical_record)
def pre_create_historical_record_callback(sender, **kwargs):
instance = kwargs["instance"]
history_instance = kwargs["history_instance"]
if isinstance(instance, ModelYouWantToRefresh)
instance.refresh_from_db()
history_instance.some_attribute = instance.some_attribute
根据 Ross Mechanic 的回答我做了一个通用的解决方案
@receiver(
pre_create_historical_record,
dispatch_uid='simple_history_refresh')
def remove_f_expressions(sender, instance, history_instance, **kwargs):
f_fields = []
for field in history_instance._meta.fields:
if isinstance(getattr(history_instance, field.name), BaseExpression):
f_fields.append(field.name)
if f_fields:
instance.refresh_from_db()
for fld_name in f_fields:
setattr(history_instance, fld_name, getattr(instance, fld_name))