【发布时间】:2018-04-02 05:27:43
【问题描述】:
我遇到了一个难以解释的问题,请多多包涵。首先,以下是相关版本,以防万一这很重要:Django 2.0.3、Python 3.6.4、PostgreSQL 10.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 是叶。因为我在上面列出的结构是一个有向无环图而不是真正的树,所以一个节点实际上可以有多个这些“完整”数字,因为从任何给定节点到它的根节点都可以有多个路径。
如果此描述没有说明,请告诉我,我可以在 Paint 中模拟一些东西。
我将覆盖CapabilityRelationship.save() 方法,因为每次更改关系时我都需要重新计算完整的数字,因为number 可能已经更改。所以我要做的就是简单地计算新的完整数字,删除所有旧的完整数字,然后保存新的数字。
我遇到的问题是我根本无法删除旧的完整号码,这让我感到莫名其妙。我想知道是否有一些关于覆盖CapabilityRelationship.save() 的东西我根本没有得到。例如:
def save(self, *args, **kwargs):
complete_numbers = self._get_complete_numbers()
print('complete numbers: {}'.format(complete_numbers))
super().save(*args, **kwargs) # Call the "real" save() method.
print('before delete: {}'.format(self.capabilityrelationshipcompletenumber_set.all()))
self.capabilityrelationshipcompletenumber_set.all().delete()
print('after delete: {}'.format(self.capabilityrelationshipcompletenumber_set.all()))
for complete_number in complete_numbers:
CapabilityRelationshipCompleteNumber.objects.create(capability_relationship=self,
complete_number=complete_number)
print('after save: {}'.format(self.capabilityrelationshipcompletenumber_set.all()))
如果我访问管理站点并将叶节点的 number 设置为 1,保存它,然后将其修改为 2,我会得到以下输出:
complete numbers: {'1.2.1.1', '2.2.1.1', '1.3.1.1', '1.1.1.1'}
before delete: <QuerySet []>
after delete: <QuerySet []>
after save: <QuerySet [<CapabilityRelationshipCompleteNumber: 1.1.1.1>, <CapabilityRelationshipCompleteNumber: 1.2.1.1>, <CapabilityRelationshipCompleteNumber: 1.3.1.1>, <CapabilityRelationshipCompleteNumber: 2.2.1.1>]>
complete numbers: {'1.1.1.2', '1.2.1.2', '2.2.1.2', '1.3.1.2'}
before delete: <QuerySet [<CapabilityRelationshipCompleteNumber: 1.1.1.1>, <CapabilityRelationshipCompleteNumber: 1.2.1.1>, <CapabilityRelationshipCompleteNumber: 1.3.1.1>, <CapabilityRelationshipCompleteNumber: 2.2.1.1>]>
after delete: <QuerySet []>
after save: <QuerySet [<CapabilityRelationshipCompleteNumber: 1.1.1.2>, <CapabilityRelationshipCompleteNumber: 1.2.1.2>, <CapabilityRelationshipCompleteNumber: 1.3.1.2>, <CapabilityRelationshipCompleteNumber: 2.2.1.2>]>
现在,所有这些看起来都很棒,但是当我访问管理站点以获取所有完整数字的列表时,我看到了到目前为止计算的所有 8 个完整数字,而不是更改后当前正确的 4 个number 到 2。如果我打开一个 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;。似乎什么都不起作用。我不明白发生了什么事。我已阅读有关删除 QuerySet 和覆盖 save() 的 Django 文档,但没有看到任何可以帮助我诊断问题的内容。
有人知道这里发生了什么吗?任何帮助是极大的赞赏。请让我知道我是否可以澄清这些。
【问题讨论】:
-
这很奇怪。您确定没有任何保存前/保存后的信号处理程序在保存
CapabilityRelationship时处理CapabilityRelationshipCompleteNumber对象吗?从您发布的内容来看,似乎正在运行其他一些代码以将它们重新插入数据库。 -
只是尝试一下,你能不能用 post_save 信号做同样的事情,看看会发生什么?
-
如果您想在保存后执行操作,您应该编写信号
post_save,而不是覆盖保存方法;如果您想在保存之前执行任何操作,请使用模型的clean方法 -
@Gahan 我看不出循环依赖是如何进入其中的。信号是一种方法,特别是如果您需要从另一个应用程序连接到模型,但是以这种方式覆盖
save()确实没有错 - 这两种方法都是有效的并且各有优点。我不认为您可以将其视为不好的做法。 -
@Geoff,我用 sqlite 数据库尝试了你的代码,完全在 django shell 中(没有管理界面),并没有发现任何问题(
>>> models.CapabilityRelationshipCompleteNumber.objects.all() <QuerySet [<CapabilityRelationshipCompleteNumber: 1.3.1.2>, <CapabilityRelationshipCompleteNumber: 1.2.1.2>, <CapabilityRelationshipCompleteNumber: 1.1.1.2>, <CapabilityRelationshipCompleteNumber: 2.2.1.2>]>)。所以问题可能出在其他代码或 postgres 中。
标签: python django postgresql django-models