Files
ZERO_CODE/export_meta_tables.py

216 lines
7.2 KiB
Python
Raw Permalink Normal View History

2026-01-23 14:48:45 +08:00
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()