import csv import os import sys from pathlib import Path def init_django(): base_dir = Path(__file__).resolve().parent project_root = base_dir / "ZeroCodeProject" / "apis" / "ZeroCodeMain" if not project_root.exists(): return None sys.path.insert(0, str(project_root)) os.environ.setdefault("DJANGO_SETTINGS_MODULE", "ZeroCodeMain.settings") try: import django django.setup() from django.db import connection from BaseApi.models import App, Model, ModelField, ModelSetting except Exception: return None return App, Model, ModelField, ModelSetting, connection def export_meta_tables(output_dir: str = None) -> None: base_dir = Path(__file__).resolve().parent if output_dir is None: output_path = base_dir else: output_path = Path(output_dir) output_path.mkdir(parents=True, exist_ok=True) all_rows = [] table1 = [ ["类名", "所在层/文件", "关键字段", "含义"], ["App", "BaseApi/models.py", "name, name_cn, module", "定义动态模型所属的应用和模块路径"], ["Model", "BaseApi/models.py", "app, name, name_cn", "定义单个动态模型的元数据(名称及所属应用)"], ["ModelField", "BaseApi/models.py", "model, name, type, is_show", "定义动态模型中的单个字段及其类型、显示属性"], ["ModelSetting", "BaseApi/models.py", "field, name, value", "定义字段的具体配置项(例如 max_length、null 等)"], ] all_rows.append(["# 元数据类汇总表"]) all_rows.extend(table1) all_rows.append([]) table2 = [ ["元数据实体", "标识字段", "对应的原对象实体", "关系说明"], ["App 记录", "name", "Django 应用(AppConfig)", "确定动态模型注册的命名空间及表名前缀"], ["Model 记录", "app + name", "动态 Django 模型类 + 物理数据表", "每条 Model 记录对应一个模型类及一张数据表"], ["ModelField 记录", "model + name", "动态模型上的一个字段", "每条记录对应模型上的一个具体字段"], ["ModelSetting 记录", "field + name", "字段构造器上的一个关键字参数", "决定字段的单个具体属性(如 max_length、null 等)"], ] all_rows.append(["# 元数据与原对象映射(概念层面)"]) all_rows.extend(table2) all_rows.append([]) django_models = init_django() if not django_models: all_file = output_path / "meta_all_in_one.csv" with all_file.open("w", newline="", encoding="utf-8-sig") as f: writer = csv.writer(f) writer.writerows(all_rows) return App, Model, ModelField, ModelSetting, connection = django_models model_rows = [ [ "app_name", "app_name_cn", "app_module", "model_name", "model_name_cn", "django_app_label", "django_model_class", "db_table_name", ] ] for m in Model.objects.select_related("app").all(): app = m.app django_app_label = app.name django_model_class = django_app_label + "." + m.name db_table_name = django_app_label + "_" + m.name model_rows.append( [ app.name, app.name_cn, app.module, m.name, m.name_cn, django_app_label, django_model_class, db_table_name, ] ) all_rows.append(["# 元数据模型与原对象模型一对一映射(实例层面)"]) all_rows.extend(model_rows) all_rows.append([]) settings_map = {} for s in ModelSetting.objects.select_related("field").all(): field_id = s.field_id pair = s.name + "=" + s.value settings_map.setdefault(field_id, []).append(pair) field_rows = [ [ "app_name", "model_name", "field_name", "field_name_cn", "field_type", "is_show", "settings", ] ] for f_obj in ModelField.objects.select_related("model", "model__app").all(): app = f_obj.model.app model = f_obj.model merged_settings = ";".join(settings_map.get(f_obj.id, [])) field_rows.append( [ app.name, model.name, f_obj.name, f_obj.name_cn, f_obj.type, "1" if f_obj.is_show else "0", merged_settings, ] ) all_rows.append(["# 字段级元数据与原对象字段映射(字段层面)"]) all_rows.extend(field_rows) all_rows.append([]) table_rows = [["table_name", "model_class", "column_count"]] model_map = { "BaseApi_app": App, "BaseApi_model": Model, "BaseApi_modelfield": ModelField, "BaseApi_modelsetting": ModelSetting, } for table_name, model_cls in model_map.items(): meta = model_cls._meta concrete_fields = [f for f in meta.get_fields() if getattr(f, "concrete", False) and not getattr(f, "many_to_many", False)] table_rows.append( [ meta.db_table, meta.label, str(len(concrete_fields)), ] ) all_rows.append(["# 元数据相关数据库表结构总览"]) all_rows.extend(table_rows) all_rows.append([]) column_rows = [ ["table_name", "model_class", "column_name", "field_type", "null", "primary_key", "default"] ] for model_cls in [App, Model, ModelField, ModelSetting]: meta = model_cls._meta for field in meta.get_fields(): if not getattr(field, "concrete", False) or getattr(field, "many_to_many", False): continue default_value = field.default if callable(default_value): default_value = "" column_rows.append( [ meta.db_table, meta.label, field.name, field.get_internal_type(), "1" if field.null else "0", "1" if field.primary_key else "0", str(default_value), ] ) all_rows.append(["# 元数据相关数据库字段结构明细"]) all_rows.extend(column_rows) all_rows.append([]) for model_cls, section_title in [ (App, "# BaseApi_app 表数据"), (Model, "# BaseApi_model 表数据"), (ModelField, "# BaseApi_modelfield 表数据"), (ModelSetting, "# BaseApi_modelsetting 表数据"), ]: meta = model_cls._meta field_names = [f.name for f in meta.fields] rows = [field_names] qs = model_cls.objects.all().values_list(*field_names) for row in qs: rows.append([str(v) for v in row]) all_rows.append([section_title]) all_rows.extend(rows) all_rows.append([]) all_file = output_path / "meta_all_in_one.csv" with all_file.open("w", newline="", encoding="utf-8-sig") as f: writer = csv.writer(f) writer.writerows(all_rows) if __name__ == "__main__": export_meta_tables()