2

我最近开始使用 Django,并且还没有停止享受 python/Django,但我目前正在努力解决一个逻辑问题。

情况(简化):

class A(models.Model):
    foo = models.CharField(max_length=255)

class B(models.Model):
    bar = models.CharField(max_length=255)
    foo =  models.ForeignKey(A)

class C(models.Model):
    title = models.CharField(max_length=255)
    bar =  models.ForeignKey(B)

class D(models.Model):
    name = models.CharField(max_length=255)
    title =  models.ForeignKey(C)
    bar =  models.ForeignKey(B)

(真正的用例由数百个这样的类组成,是的,这是一团糟,它清楚地证明了糟糕的数据库设计,但我对此无能为力)

我在每个班级都创建了动态 ModelForms。一般目的是检索一个 excel 文件并将它们插入到字段验证等中的正确 ModelForms 中。每个 excel 文件都有多个映射到类的工作表,第一行(标题)描述模型字段,所有其他行表示数据。

数据完全未排序,因此通常不破坏外键序列的插入顺序是 A => B => C => D。但在这种情况下,整个序列可能像 D => B => C => A . 当我验证第一张由于尚未定义相关外键而未验证的表 D 时,问题就出现了。

问题是,如何添加所有数据并在之后验证参照完整性?

提前致谢!


谢谢你的帮助!

实际上所有的主键都是从根模型派生的,根模型保存着所有子表的映射表。我在第一篇文章中没有提到它,因为我想保持情况简单。话虽如此,我无法更改(混乱!),也无法重新设计映射到任何现有(混乱!)数据库的类。为了完成这个混乱,每个字段都设置为“not Null”。

我的第二个想法是最初填充一个映射表(还没有真正的想法如何做到这一点),并以此对传入的数据进行排序。听起来像猴子工作,很脏,我自己也不喜欢这个主意,我希望有更聪明的方法。

你对这个问题的任何数学解决方案有任何提示吗?这就像在任意数据上生成一棵树。

更新:

我做了两个函数来解决这个问题,还没有测试错误处理。

validate_tables:查找与给定应用程序相关的所有表,并将嵌套列表 (self.found_fields) 保存在 dict (child: [parent, parent, (...)]) 中。

gen_sequence:写入一个列表(self.sequence),其中包含到 object_names 的正确序列映射。

欢迎认可!

这是我目前的解决方案(得到这个想法的片段)

    def validate_tables(self):
        app = get_app("testdata")
        self.sequence = []
        self.found_fields = {}
        for model in get_models(app):
            hits = []
            for local_field in model._meta.local_fields:
                if isinstance(local_field, models.ForeignKey):
                    hits.append(local_field.related.parent_model._meta.object_name)
            self.found_fields.update({model._meta.object_name: hits})
        if self.gen_sequence():
            return True
        else:
            raise self.sequence_errors


    def gen_sequence(self, unresolved=None):

        if unresolved:
            self.found_fields = unresolved
            unresolved = {}
        else:
            unresolved = {}

        for model in self.found_fields:
            if ((all(parent in self.sequence for parent in self.found_fields[model]) 
                 and self.sequence)
                or not self.found_fields[model]):
                self.sequence.append(model)
            else:
                unresolved.update({model: self.found_fields[model]})

        if unresolved == self.found_fields:
            self.sequence_errors = unresolved
            return False
        elif not unresolved:
            return self.gen_sequence
        else:
            return self.gen_sequence(unresolved)
4

1 回答 1

0

您将需要定义自己的主键,我认为您有一个合适的字段,否则不会发生此问题,并且还允许 ForeignKey 为空。困难的部分将是稍后建立参照完整性,这在 Django中很难但似乎并非不可能。

相反,我将有两个字段,一个是您的虚拟主键并使您当前的外键可以为空:

class A(models.Model):
    foo = models.CharField(max_length=255)

class B(models.Model):
    bar = models.CharField(max_length=255)
    foo =  models.ForeignKey(A, null=True)
    foo_key =  models.CharField()

然后,在数据导入后找到所有带有 foo_key 的 'B' 对象,建立关系并将 foo_key 设置为 null。

这是我将大量数据从以前的 GAE 项目导入 PostgreSQL 数据库时使用的机制。

于 2013-01-15T20:38:36.497 回答