【问题标题】:How to determine the sequence order of SQL inserts within multiple foreign keys?如何确定多个外键中 SQL 插入的顺序?
【发布时间】:2012-12-30 00:23:54
【问题描述】:

我最近开始使用 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。

欢迎批准!

这是我目前的解决方案(sn-p 得到这个想法)

    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)

【问题讨论】:

  • 这是一次性导入吗?还是您会定期这样做?
  • 它会定期发生。
  • 哎哟。是的,我会考虑要么按照我的建议去做,要么比外键更松散的连接(即总是查找值)。此外,在紧要关头,您可以使用模型继承形式的巨型查找表,使用基础对象建立连接,然后稍后实例化细节docs.djangoproject.com/en/dev/topics/db/models/…

标签: python django


【解决方案1】:

您需要定义自己的主键,我认为您有一个合适的字段,否则不会发生此问题,并且还允许 ForeignKey 为空。困难的部分将是稍后在 Django 中建立引用完整性,which is difficult but seemingly not impossible to do

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

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 数据库时使用的机制。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-19
    • 2021-03-20
    • 2018-08-07
    • 2021-12-23
    相关资源
    最近更新 更多