【发布时间】:2011-10-18 17:16:23
【问题描述】:
我有一个模型,它与自身具有多对多关系。
我想在模型上创建一个验证,以防止一个组成为它自己的子组,或者它的子组的子组等。目的是防止可能导致循环/无限的情况递归。
我已经尝试在模型 clean() 方法中实现这一点,如下所示。
我也尝试在模型 save() 方法中使用事务来实现这一点。
在这两种情况下,我都遇到了无效更改(错误地)保存到数据库的情况,但是如果我尝试对任一实例进行进一步更改,验证会检测到错误,但此时,坏数据已经在数据库中了。
我想知道这是否可行,如果可以,是否可以在模型验证中这样做,所以我不必确保团队中的每个人都记得从他们在未来。
事不宜迟,代码:
class Group(models.Model):
name = models.CharField(max_length=200)
sub_groups = models.ManyToManyField('self', through='SubGroup', symmetrical=False)
def validate_no_group_loops(self, seen=None):
if seen is None:
seen = []
if self.id in seen:
raise ValidationError("LOOP DETECTED")
seen.append(self.id)
for sub_group in self.target.all():
sub_group.target.validate_no_group_loops(seen)
# I thought I would use the standard validation mechanism in the clean()
# method, but it appears that when I recurse back to the group I started
# with, I do so with a query to the database which retreives the data before
# it's been modified. I'm still not 100% sure if this is the case, but
# regardless, it does not work.
def clean(self):
self.validate_no_group_loops()
# Suspecting that the problem with implementing this in clean() was that
# I wasn't testing the data with the pending modifications due to the
# repeated queries to the database, I thought that I could handle the
# validation in save(), let the save actually put the bad data into the
# database, and then roll back the transaction if I detect a problem.
# This also doesn't work.
def save(self, *args, **kwargs):
super(Group, self).save(*args, **kwargs)
try:
self.validate_no_group_loops()
except ValidationError as e:
transaction.rollback()
raise e
else:
transaction.commit()
class SubGroup(models.Model):
VERBS = { '+': '+', '-': '-' }
action = models.CharField(max_length=1, choices=VERBS.items(), default='+')
source = models.ForeignKey('Group', related_name='target')
target = models.ForeignKey('Group', related_name='source')
提前感谢您提供的任何帮助。
[edit] 仅供参考,如果您无法根据我用来管理事务的机制来判断,我目前使用的是 django 1.2,因为这是用于 RHEL6 的 fedora EPEL 存储库中可用的。如果有可用的解决方案但需要升级到 1.3,我升级没有问题。我也在使用 python 2.6.6,因为这是 RedHat 的 RHEL6 中可用的。我宁愿避免 python 升级,但我非常怀疑它是否相关。
【问题讨论】:
-
您能解释一下为什么使用多对多关系吗?我有树状结构并使用这种模式:如果这个父节点为空,每个节点都有一个属性“父节点”,这是一个根节点。或者你可以使用这个github.com/django-mptt/django-mptt
-
在我的应用程序中,它不是一棵树。一个组可以包含在许多其他组中,并且一个组可以包含许多组。这也是我的应用程序的简化版本,它表达了问题,而不会给每个人带来大量无关代码的负担,这就是为什么它需要一个直通模型来定义有关子组关系的参数。
-
如果您查看我在my question 中链接的django_dag 项目,您会注意到在models.py 第195 行中有一个名为
@staticmethod的circular_checker,您可能能够在他的代码中找到一些灵感。 -
实际上,我注意到了你的问题,意识到我也在尝试实施一个 dag,检查了那个项目,然后自己实施了一个,从那个项目中大量窃取。如果您想在这里提交一些类似的内容作为答案,我会为您提供正确答案。
-
j_syk 如果您提交推荐 django_dag 的答案,我一定会接受。
标签: python django django-models django-validation