Автоматическое слияние произвольных моделей Django - PullRequest
2 голосов
/ 10 ноября 2011

У меня есть две управляемые базы данных 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 для преобразования ссылок на внешние ключи в исходной таблице в эквивалентные идентификаторы в целевой таблице.

Подводя итог, можно сказать, что алгоритм будет перебирать таблицу для объединения в порядке зависимости, причем сначала перебираются родители. Поэтому, если мы хотим объединить таблицы auth_users и mycustomprofile, которые зависят от auth_users, мы будем выполнять итерации ['auth_users', 'mycustomprofile'].

Каждая объединенная таблица должна иметь своего рода индикатор, документирующий комбинацию столбцов, которая обозначает универсально уникальную запись (то есть «ключ»). Для auth_users это может быть столбец «username» и / или «email».

Если значение ключа в базе данных B уже существует в A, то запись не импортируется из B, но записывается идентификатор существующей записи в A.

Если значение ключа в базе данных B не существует в A, то запись импортируется из B, и записывается идентификатор новой записи.

Используя ранее записанный идентификатор, создается сопоставление, объясняющее, как сопоставить ссылки внешнего ключа на эту конкретную запись в B с новой объединенной / ранее существующей записью в A. Когда будущие записи объединяются в A, это сопоставление будет использоваться для преобразования внешних ключей.

Я все еще мог бы представить некоторые случаи, когда импортированная запись ссылается на таблицу, не включенную в dumpdata, что может привести к сбою всего импорта, поэтому для моделирования импорта потребуется какая-то опция "dryrun", чтобы гарантировать весь FK ссылки могут быть переведены.

Это похоже на практический подход? Есть ли лучший способ?

РЕДАКТИРОВАТЬ: Это не совсем то, что я ищу, но я думал, что другие могут найти это интересным. Проект Turbion имеет механизм копирования изменений между эквивалентными записями в разных моделях Django в одной базе данных. Он работает, определяя слой перевода (т.е. merging.ModelLayer) между двумя моделями Django, поэтому, скажем, если вы обновите поле «www» в профиле пользователя bob@bob.com, оно автоматически обновит поле «url» в пользователе Другой профиль bob@bob.com.

Функциональность, которую я ищу, немного отличается, в том смысле, что я хочу объединять весь (или частичный) снимок базы данных через нечастые интервалы, в некотором роде, как это делает команда управления загрузкой данных.

1 Ответ

1 голос
/ 10 ноября 2011

Ничего себе. Это будет сложная работа в любом случае. Это сказало:

Если я правильно понимаю потребности вашего проекта, это можно сделать с помощью переноса данных в Юг . Тем не менее, я бы солгал, если бы сказал, что это будет шутка.

Моя рекомендация - и это в основном попугай предположения в вашем вопросе, но я хочу прояснить - что у вас есть одна «главная» таблица, которая является базой и в которой есть записи из другого таблица добавлена ​​к нему. Таким образом, таблица 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

Это приведет к миграции всех B, не принадлежащих A, к A, и предоставит вам таблицу B, которые являются ожидаемыми дубликатами, которые вы могли бы затем проверить другими способами перед удалением.

Как я уже сказал, этот образец не является завершенным. Если вы решите пойти по этому пути, потратьте время на документацию South и, в частности, обратите внимание на миграцию данных .

Это мои 2 ¢. Надеюсь, это поможет.

...