from os.path import exists from importlib.machinery import SourceFileLoader from django.apps import apps from collections import OrderedDict from django.db.models import OuterRef, Subquery import transfer.models as legacy_models # from pprint import pprint as print def transfer_objects(data_type): models_list = {} for app in apps.get_app_configs(): if exists(f"{app.path}/transfer.py"): card_module = SourceFileLoader("transfer", f"{app.path}/transfer.py").load_module() if not hasattr(card_module, "card") or len(card_module.card) < 1: continue for model, card in card_module.card.items(): if "data_type" in card and data_type == card["data_type"]: card['app_label'] = app.label models_list[model] = card if len(models_list) < 1: print(f"Models with data type {data_type} not found in structure") exit(1) models_list = sort_by_dependencies(models_list) legacy_objects = OrderedDict() for model, card in models_list.items(): legacy_objects[model] = get_model_data(model, card) def sort_by_dependencies(data): """Сортировка моделей по зависимостям""" """Сначала мы сортируем модели по зависимостям в обратном порядке, используя сортировку вставкой""" result = [] for model, card in data.items(): if "dependencies" in card and isinstance(card['dependencies'], tuple): for model_dependency in result: if model_dependency in card['dependencies']: result.insert(result.index(model_dependency), model) break else: result.append(model) """Затем мы создаём сортированный словарь из реверса получившегося результата""" result = OrderedDict( [(model, data[model]) for model in reversed(result)] ) return result def get_model_data(model, card): # print(f"=================================================\r\n" # f"MODEL: {model}\r\n" # f"=================================================") card_fields = card['fields'] relations = card_fields['relations'] if 'relations' in card_fields else None if relations: del(card_fields['relations']) if len(card_fields) > 1: print("You must set one table to transfer") return legacy_table = list(card_fields.keys())[0] fields = list(card_fields.values())[0] try: legacy_model = apps.get_model(app_label="transfer", model_name=legacy_table) except LookupError as e: print(f"ERROR: legacy model {model} can not be loaded: {e}") return legacy_fields = [] for legacy_field in fields.values(): if isinstance(legacy_field, str): legacy_fields.append(legacy_field) elif isinstance(legacy_field, tuple): if isinstance(legacy_field[0], tuple): """Группа полей""" for legacy_field_group in legacy_field: legacy_fields.append(legacy_field_group[1]) else: """Одиночное поле""" legacy_fields.append(legacy_field[1]) #TODO: убрать после совместимости if model != "City": return queryset = legacy_model.objects.only(*legacy_fields) """Проверяем наличие внешних связей""" if relations: queryset = get_relation_data(relations, queryset, fields) return get_transfer_data(model, card, queryset) def get_relation_data(relations, queryset, fields): # related_legacy_models = [x.related_model._meta.model.__name__ for x in legacy_model._meta.related_objects] """Получаем названия внешних зависимостей""" for relation_table, relation_data in relations.items(): """Если объекты связаны через ForeignKey, то ничего не надо делать - все аннотации уже подключены""" if isinstance(relation_data['key'], tuple): """Вытаскиваем relation по id""" try: child_legacy_model = apps.get_model(app_label="transfer", model_name=relation_table) except LookupError as e: raise LookupError(f"ERROR: legacy model {relation_table} can not be loaded: {e}") child_table_queryset = child_legacy_model.objects.filter( city_id=OuterRef('pk') ).only(*relation_data['fields'].values()) queryset = queryset.annotate(**{ f"{relation_table.lower()}": Subquery(child_table_queryset) }) return queryset def get_transfer_data(model, card, queryset): from pprint import pprint as print try: app_model = apps.get_model(app_label=card['app_label'], model_name=model) except LookupError as e: print(f"ERROR: model {model} from {card['app_label']} can not be loaded: {e}") return if "fields" not in card or not isinstance(card['fields'], dict) or len(card['fields']) != 1: raise ValueError("You must set correct fields to transfer") for app_field, legacy_field_data in card['fields'].items(): print(app_field) print(legacy_field_data) print("*************************************************************************") # print(app_model) # print("*************************************************************************") # print(queryset) return queryset