自动合并任意Django模型

时间:2011-11-09 20:52:59

标签: python django database-design django-models django-orm

我有两个Django-ORM托管数据库,我想合并。两者都有一个非常相似的模式,并且都有标准的auth_users表,以及一些相互引用的其他共享表以及auth_users,我想自动将它们合并到一个数据库中。

可以理解,根据外键关系以及每个表中“唯一”记录的构成,这可能非常重要。

有谁知道是否存在执行此合并操作的工具?

如果当前不存在这样的情况,我正在考虑根据标准的loaddata命令编写自己的管理命令。实质上,您将使用标准dumpdata命令从源数据库导出表,然后使用已修改版本的loaddata将它们“合并”到目标数据库中。

例如,如果我有数据库A和B,并且我想将数据库B合并到数据库A中,那么我想根据伪代码遵循一个过程:

merge_database_dst = A
merge_database_src = B
for table in sorted(merge_database_dst.get_redundant_tables(merge_database_src), key=acyclic_dependency):
    key = table.get_unique_column_key()
    src_id_to_dst_id = {}
    for record_src in merge_database_src.table.objects.all():
        src_key_value = record_src.get_key_value(key)
        try:
            record_dst = merge_database_dst.table.objects.get(key)
            dst_key_value = record_dst.get_key_value(key)
        except merge_database_dst.table.DoesNotExist:
            record_dst = merge_database_dst.table(**[(k,convert_fk(v)) for k,v in record_src._meta.fields])
            record_dst.save()
            dst_key_value = record_dst.get_key_value(key)
        src_id_to_dst_id[(table,record_src.id)] = record_dst.id

convert_fk()函数将使用src_id_to_dst_id索引将源表中的外键引用转换为目标表中的等效ID。

总而言之,算法将迭代表以依赖顺序合并,父对象首先迭代。因此,如果我们想要合并表auth_users和mycustomprofile,这取决于auth_users,我们将迭代['auth_users','mycustomprofile']。

每个合并表都需要某种指示符来记录表示通用唯一记录(即“密钥”)的列组合。对于auth_users,可能是“用户名”和/或“电子邮件”列。

如果数据库B中的密钥值已存在于A中,则不会从B导入记录,而是记录A中现有记录的ID。

如果A中不存在数据库B中的密钥值,则从B导入记录,并记录新记录的ID。

使用先前记录的ID,创建映射,解释如何将对B中的特定记录的外键引用映射到A中的新合并/预先存在的记录。当将来的记录合并到A中时,此映射将用于转换外键。

我仍然可以设想一些导入记录引用dumpdata中未包含的表的情况,这可能导致整个导入失败,因此需要某种“dryrun”选项来模拟导入以确保所有FK引用可以翻译。

这看起来像是一种实用的方法吗?还有更好的方法吗?

编辑:这不是我正在寻找的,但我认为其他人可能会觉得有趣。 Turbion project有一种机制,用于在同一数据库中的不同Django模型中的等效记录之间复制更改。它的工作原理是在两个Django模型之间定义一个转换层(即merging.ModelLayer),所以,如果你更新用户bob@bob.com的个人资料中的“www”字段,它会自动更新用户的“url”字段bob@bob.com的其他资料。

我正在寻找的功能有点不同,因为我想以不频繁的间隔合并整个(或部分)数据库快照,这与loaddata管理命令的方式类似。

1 个答案:

答案 0 :(得分:1)

哇。无论如何,这将是一项复杂的工作。那说:

如果我正确理解了项目的需求,可以使用South中的数据迁移来完成。即便如此,如果我说这将是一个笑话,我会撒谎。

我的建议是 - 这在你的问题中主要是一个假设的鹦鹉,但我想说清楚 - 你有一个“主”表是基础,并且有来自另一个的记录表添加到它。因此,表A保留所有现有记录,并且只从B. B中添加添加到A中的添加,一旦完成,B就被删除。

我犹豫是否要为您编写示例代码,因为您的实际工作将比这复杂得多,但无论如何我都会尝试指出您正确的方向。考虑一下像......

import datetime
from south.db import db
from south.v2 import DataMigration
from django.db import models

class Migration(DataMigration):
    def forwards(self, orm):
        for b in orm.B.objects.all():
            # sanity check: does this item get copied into A at all?
            if orm.A.objects.filter(username=b.username):
                continue

            # make an A record with the properties of my B record
            a = orm.A(
                first_name=b.first_name,
                last_name=b.last_name,
                email_address=b.email_address,
                [...]
            )

            # save the new A record, and delete the B record
            a.save()
            b.delete()

    def backwards(self, orm):
        # backwards method, if you write one

这最终会将所有不在A中的B迁移到A,并留下一个预期重复的B表,然后在删除之前可以通过其他方式检查。

就像我说的那样,这个样本并不是完整的。如果您决定采用此路线,请花时间在South documentation,尤其要确保查看data migrations

那是我的2¢。希望它有所帮助。