From bb608b44de286b6c9cff01a3420c353416cb2167 Mon Sep 17 00:00:00 2001 From: vermouth789 <115351183+vermouth789@users.noreply.github.com> Date: Fri, 23 Jan 2026 14:48:45 +0800 Subject: [PATCH] =?UTF-8?q?=E9=A6=96=E6=AC=A1=E4=B8=8A=E4=BC=A0=E5=AE=8C?= =?UTF-8?q?=E6=95=B4=E9=A1=B9=E7=9B=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- export_meta_tables.py | 215 ++++++++++++++++++++++++++++++++++++++++++ meta_all_in_one.csv | 14 +++ 2 files changed, 229 insertions(+) create mode 100644 export_meta_tables.py create mode 100644 meta_all_in_one.csv diff --git a/export_meta_tables.py b/export_meta_tables.py new file mode 100644 index 00000000..18fed36d --- /dev/null +++ b/export_meta_tables.py @@ -0,0 +1,215 @@ +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() + diff --git a/meta_all_in_one.csv b/meta_all_in_one.csv new file mode 100644 index 00000000..e28c12df --- /dev/null +++ b/meta_all_in_one.csv @@ -0,0 +1,14 @@ +# 元数据类汇总表 +类名,所在层/文件,关键字段,含义 +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 等) + +# 元数据与原对象映射(概念层面) +元数据实体,标识字段,对应的原对象实体,关系说明 +App 记录,name,Django 应用(AppConfig),确定动态模型注册的命名空间及表名前缀 +Model 记录,app + name,动态 Django 模型类 + 物理数据表,每条 Model 记录对应一个模型类及一张数据表 +ModelField 记录,model + name,动态模型上的一个字段,每条记录对应模型上的一个具体字段 +ModelSetting 记录,field + name,字段构造器上的一个关键字参数,决定字段的单个具体属性(如 max_length、null 等) +