Django:删除与ForeignKey相关的实例后如何保存模型实例?

时间:2018-09-20 16:48:04

标签: python django django-models

我正在使用Django 2.1.1。

我有一个模型Analysis,其中除其他字段外,还包含一个MyFile模型(我为处理文件而编写的模型)的ForeignKey:

from polymorphic.models import PolymorphicModel
from django.db.models import Model, DateTimeField, FileField, SET_NULL
from django.db.models.signals import pre_delete

class MyFile(Model):
    file = FileField(upload_to='./', null=False, blank=False)
    description = CharField(max_length=255, null=True, blank=True)
    date_added = DateTimeField(auto_now_add=True)


@receiver(pre_delete, sender=MyFile)
def mymodel_delete(sender, instance, **kwargs):
    """
    To delete the file connected to the `sender` class: receive the pre_delete signal
    and delete the file associated with the model instance.
    """
    instance.file.delete(False)

class Analysis(PolymorphicModel):
        # ... other fields ...
        file_results = ForeignKey(MyFile, on_delete=SET_NULL,
                                  related_name='file_results', 
                                  null=True, blank=True)

AnalysisPolymorphicModel,原因与大型项目有关。

Analysis.file_results中设置on_delete=SET_NULL是因为我希望即使没有Analysis也可以存在一个file_result实例,以后可以填充它。

我们假设我添加了一些文件(MyFile表有几行)和一些Analysis实例。现在,如果我要删除与Analysis实例之一相关的文件,请执行以下操作:

a = Analysis.objects.get(pk=0)
a.file_results.delete()
a.save()

但出现以下错误:

File "/Users/mtazzari/djangos/views.py" in update_job_refs
  377.             a.save()

File "/Users/mtazzari/anaconda/envs/djangos/lib/python3.6/site-packages/polymorphic/models.py" in save
  83.         return super(PolymorphicModel, self).save(*args, **kwargs)

File "/Users/mtazzari/anaconda/envs/djangos/lib/python3.6/site-packages/django/db/models/base.py" in save
  670.                         "unsaved related object '%s'." % field.name
ValueError: save() prohibited to prevent data loss due to unsaved
            related object 'file_results'.

mymodel_delete信号上调用的pre_delete函数可以正常工作,因为实际上已从文件系统中删除了文件。 但是,我真的不了解如何解决ValueError

有趣的是,我注意到以下几行工作正常,即不引发任何ValueError异常,将文件从文件系统中删除,并将a.file_results中的FK设置为​​Null:

a = Analysis.objects.get(pk=0)
tmp = a.file_results
a.file_results = None    
tmp.file_results.delete()
a.save()

但是,这是正确的方法吗?删除相关对象的最佳做法是什么?

谢谢!

1 个答案:

答案 0 :(得分:1)

首先,请注意,您不必仅因为save()delete()delete()将根据需要更新数据库。

也就是说,继续使用实例进行其他操作是合理的,从而导致save()。出现错误的原因是a.file_results Python对象仍然存在,并且引用了现在丢失的数据库行。 documentation for delete()提到了这一点:

  

这只会删除数据库中的对象; Python实例将仍然存在,并且其字段中仍将包含数据。

因此,如果您想继续使用实例对象,只需自行将属性设置为None。与上面的代码类似,除了不需要临时对象。

a = Analysis.objects.get(pk=0)
a.file_results.delete()
a.file_results = None

# ... more operations on a

a.save()  # no error