从中删除关联的模型实例模型.保存()不工作

2024-06-02 14:06:25 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个问题有点难以解释,请原谅我。首先,这里有一些相关的版本,以防出现问题:Django 2.0.3、python3.6.4、postgresql10.3。在

基本上,我对我的一些Django模型有这样的结构:

class Capability(models.Model):
    relationships = models.ManyToManyField('self',
                                           symmetrical=False,
                                           through='CapabilityRelationship',
                                           blank=True)
    name = models.CharField(max_length=255)


class CapabilityRelationship(models.Model):
    from_capability = models.ForeignKey(Capability,
                                        on_delete=models.CASCADE,
                                        related_name='from_set',
                                        blank=True,
                                        null=True)
    to_capability = models.ForeignKey(Capability,
                                      on_delete=models.CASCADE,
                                      related_name='to_set')
    number = models.PositiveSmallIntegerField(validators=[MinValueValidator(1)])

    def _get_complete_numbers(self):
        # generate the complete numbers using depth-first search

    def save(self, *args, **kwargs):
        # Get the complete numbers before saving. If we're not able to generate the complete numbers, something is
        # wrong, an error will be raised, and we don't want to save.
        complete_numbers = self._get_complete_numbers()

        super().save(*args, **kwargs)  # Call the "real" save() method.

        # Delete all old complete numbers.
        self.capabilityrelationshipcompletenumber_set.all().delete()

        # Save the new complete numbers.
        for complete_number in complete_numbers:
            CapabilityRelationshipCompleteNumber.objects.create(capability_relationship=self,
                                                                complete_number=complete_number)


class CapabilityRelationshipCompleteNumber(models.Model):
    capability_relationship = models.ForeignKey(CapabilityRelationship, on_delete=models.CASCADE)
    complete_number = models.CharField(max_length=255, unique=True)

为了用文字描述这些模型,我有一个Capability模型,它与自身有多对多的关系(在CapabilityRelationship中捕获)。实际上,这将是一个“树”,其中每个节点可以有多个子节点多个父节点(即,它是一个有向无环图)。最后,每个关系实例可以有多个“完整数字”(在CapabilityRelationshipCompleteNumber中捕获)。在

“数字”和“完全数”背后的概念本质上是杜威十进制。完整的1.2.3.4将有4个级别的Capability对象,其中1是最顶层(即根节点),4是叶。因为我在上面列出的结构是一个DAG而不是一棵树,一个节点实际上可以有多个这样的“完整”数字,因为从任何给定的节点到它的根都可以有多条路径。在

如果这个描述不成立,请让我知道,我可以模仿一些东西在油漆。在

我重写了CapabilityRelationship.save()方法,因为每次关系更改时都需要重新计算完整的数字,因为number可能已经更改。所以我要做的就是简单地计算新的完整数,删除所有旧的完整数,然后保存新的。在

我遇到的问题是我根本无法删除旧的完整数字,这让我很困惑。我想知道是否有什么关于重写CapabilityRelationship.save()的东西我根本就不明白。例如:

^{pr2}$

如果我访问管理站点并将叶节点的number设置为1,保存它,然后将其修改为2,我得到以下输出:

^{3}$

现在,所有这些看起来都很好,但是当我访问管理站点以获得所有完整数字的列表时,我看到到目前为止计算的所有8个完整数字,而不是将number更改为2后当前正确的4。如果我打开一个Python shell并列出完整的数字,我会看到到目前为止创建的所有数字:

> ./manage.py shell
Python 3.6.4 (default, Dec 19 2017, 15:24:51)
[GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.39.2)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from src.apps.api.models.capability import *
>>> CapabilityRelationshipCompleteNumber.objects.all()
<QuerySet [<CapabilityRelationshipCompleteNumber: 1.1.1.1>, <CapabilityRelationshipCompleteNumber: 1.1.1.2>, <CapabilityRelationshipCompleteNumber: 1.2.1.1>, <CapabilityRelationshipCompleteNumber: 1.2.1.2>, <CapabilityRelationshipCompleteNumber: 1.3.1.1>, <CapabilityRelationshipCompleteNumber: 1.3.1.2>, <CapabilityRelationshipCompleteNumber: 2.2.1.1>, <CapabilityRelationshipCompleteNumber: 2.2.1.2>]>

如果我使用psql直接查看数据库,我会看到同样的情况。在

显然,无论出于什么原因,delete调用实际上并没有发生。我尝试过CapabilityRelationshipCompleteNumber.objects.filter(capability_relationship=self).delete()CapabilityRelationshipCompleteNumber.objects.all().delete(),并使用connection.cursor()发出一个原始SQL DELETE FROM api_capabilityrelationshipcompletenumber;。似乎什么都不管用。我不明白发生了什么事。我已经阅读了Django文档中关于删除查询集并重写save(),但是我没有看到任何可以帮助我诊断问题的东西。在

有人知道这是怎么回事吗?非常感谢任何帮助。请让我知道,如果我可以澄清任何这一点。在


Tags: theselftruenumber节点modelssave数字
1条回答
网友
1楼 · 发布于 2024-06-02 14:06:25

好吧,我是在一整天的网络调试和挖掘之后发现的。据我所知,问题似乎出在Django如何挽救许多关系上。如https://stackoverflow.com/a/1925784/1269634所述:

When you save a model via admin forms it's not an atomic transaction. The main object gets saved first (to make sure it has a PK), then the M2M is cleared and the new values set to whatever came out of the form. So if you are in the save() of the main object you are in a window of opportunity where the M2M hasn't been updated yet. In fact, if you try to do something to the M2M, the change will get wiped out by the clear(). I ran into this about a year ago.

尽管这篇文章最初可以追溯到2009年,但这种烦恼在django2.0中显然仍然存在。看完这篇文章,我相信这就是造成这种奇怪的原因。不幸的是,据我所知,Django文档中没有记录这一点,这就是为什么花了这么长时间才找到并修复的原因。在

解决这个问题的方法变得相对简单。我有效地将CapabilityRelationship.save()重命名为CapabilityRelationship.update_complete_numbers()。然后,我修改了Capability管理实现来覆盖^{}。管理实现现在如下所示:

@admin.register(Capability)
class CapabilityAdmin(nested_admin.NestedModelAdmin):
    list_display = ['name']
    search_fields = ['name']
    inlines = [CapabilityRelationshipInlineAdmin]

    def save_related(self, request, form, formsets, change):
        super().save_related(request, form, formsets, change)

        for capability_relationship in form.instance.to_set.all():
            capability_relationship.update_complete_numbers()

这很管用!神奇的是,更新完整的数字发生在模型保存完成之后(当super().save_related(request, form, formsets, change)返回时是真的)。在

感谢大家的帮助!我希望这最终能帮助其他人。在

相关问题 更多 >