161 lines
5.4 KiB
Python
161 lines
5.4 KiB
Python
"""匠厂数据管理展示元数据(``_jiangchang_tables`` / ``_jiangchang_columns``)。
|
||
|
||
SQLite 没有原生表/字段 COMMENT,宿主「数据管理」页面靠这两张元数据表
|
||
拿到中文表名/列名/可编辑性等。字段顺序由 ``CREATE TABLE`` + ``PRAGMA table_info``
|
||
的 ``cid`` 决定,不读 ``display_order``。
|
||
|
||
本文件是模板 ``scripts/db/display_metadata.py`` 的最小可用子集:
|
||
- ``init_display_metadata_tables``:建元数据表
|
||
- ``upsert_table_metadata`` / ``upsert_column_metadata``:写入展示元数据
|
||
- ``list_physical_column_names``:按 cid 列出物理字段名
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
from typing import Iterable
|
||
|
||
# 宿主隐藏这些表,不作为业务表展示
|
||
INTERNAL_METADATA_TABLES = frozenset({"_jiangchang_tables", "_jiangchang_columns"})
|
||
|
||
_JIANGCHANG_TABLES_DDL = """
|
||
CREATE TABLE IF NOT EXISTS _jiangchang_tables (
|
||
table_name TEXT PRIMARY KEY,
|
||
display_name TEXT NOT NULL,
|
||
description TEXT,
|
||
grid_help TEXT,
|
||
visible INTEGER NOT NULL DEFAULT 1,
|
||
readonly INTEGER NOT NULL DEFAULT 0,
|
||
default_sort_json TEXT,
|
||
default_visible_columns_json TEXT
|
||
)
|
||
"""
|
||
|
||
_JIANGCHANG_COLUMNS_DDL = """
|
||
CREATE TABLE IF NOT EXISTS _jiangchang_columns (
|
||
table_name TEXT NOT NULL,
|
||
column_name TEXT NOT NULL,
|
||
display_name TEXT NOT NULL,
|
||
description TEXT,
|
||
grid_help TEXT,
|
||
display_order INTEGER,
|
||
visible INTEGER NOT NULL DEFAULT 1,
|
||
searchable INTEGER NOT NULL DEFAULT 1,
|
||
editable INTEGER NOT NULL DEFAULT 1,
|
||
display_type TEXT,
|
||
options_json TEXT,
|
||
PRIMARY KEY (table_name, column_name)
|
||
)
|
||
"""
|
||
|
||
|
||
def init_display_metadata_tables(cursor) -> None:
|
||
cursor.execute(_JIANGCHANG_TABLES_DDL)
|
||
cursor.execute(_JIANGCHANG_COLUMNS_DDL)
|
||
|
||
|
||
def upsert_table_metadata(
|
||
cursor,
|
||
*,
|
||
table_name: str,
|
||
display_name: str,
|
||
description: str | None = None,
|
||
grid_help: str | None = None,
|
||
visible: int = 1,
|
||
readonly: int = 0,
|
||
default_sort_json: str | None = None,
|
||
default_visible_columns_json: str | None = None,
|
||
) -> None:
|
||
cursor.execute(
|
||
"""
|
||
INSERT INTO _jiangchang_tables (
|
||
table_name, display_name, description, grid_help, visible, readonly,
|
||
default_sort_json, default_visible_columns_json
|
||
)
|
||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||
ON CONFLICT(table_name) DO UPDATE SET
|
||
display_name = excluded.display_name,
|
||
description = excluded.description,
|
||
grid_help = excluded.grid_help,
|
||
visible = excluded.visible,
|
||
readonly = excluded.readonly,
|
||
default_sort_json = excluded.default_sort_json,
|
||
default_visible_columns_json = excluded.default_visible_columns_json
|
||
""",
|
||
(
|
||
table_name, display_name, description, grid_help, visible, readonly,
|
||
default_sort_json, default_visible_columns_json,
|
||
),
|
||
)
|
||
|
||
|
||
def upsert_column_metadata(
|
||
cursor,
|
||
*,
|
||
table_name: str,
|
||
column_name: str,
|
||
display_name: str,
|
||
description: str | None = None,
|
||
grid_help: str | None = None,
|
||
visible: int = 1,
|
||
searchable: int = 1,
|
||
editable: int = 1,
|
||
display_type: str | None = None,
|
||
options_json: str | None = None,
|
||
) -> None:
|
||
cursor.execute(
|
||
"""
|
||
INSERT INTO _jiangchang_columns (
|
||
table_name, column_name, display_name, description, grid_help,
|
||
display_order, visible, searchable, editable, display_type, options_json
|
||
)
|
||
VALUES (?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?)
|
||
ON CONFLICT(table_name, column_name) DO UPDATE SET
|
||
display_name = excluded.display_name,
|
||
description = excluded.description,
|
||
grid_help = excluded.grid_help,
|
||
display_order = NULL,
|
||
visible = excluded.visible,
|
||
searchable = excluded.searchable,
|
||
editable = excluded.editable,
|
||
display_type = excluded.display_type,
|
||
options_json = excluded.options_json
|
||
""",
|
||
(
|
||
table_name, column_name, display_name, description, grid_help,
|
||
visible, searchable, editable, display_type, options_json,
|
||
),
|
||
)
|
||
|
||
|
||
def seed_column_metadata_batch(
|
||
cursor,
|
||
*,
|
||
table_name: str,
|
||
columns: Iterable[dict[str, object]],
|
||
) -> None:
|
||
"""批量 upsert 字段元数据。"""
|
||
for column in columns:
|
||
upsert_column_metadata(
|
||
cursor,
|
||
table_name=table_name,
|
||
column_name=str(column["column_name"]),
|
||
display_name=str(column["display_name"]),
|
||
description=str(column["description"]) if column.get("description") else None,
|
||
grid_help=str(column["grid_help"]) if column.get("grid_help") else None,
|
||
visible=int(column.get("visible", 1)),
|
||
searchable=int(column.get("searchable", 1)),
|
||
editable=int(column.get("editable", 1)),
|
||
display_type=str(column["display_type"]) if column.get("display_type") else None,
|
||
options_json=str(column["options_json"]) if column.get("options_json") else None,
|
||
)
|
||
|
||
|
||
def _quote_identifier(name: str) -> str:
|
||
return '"' + name.replace('"', '""') + '"'
|
||
|
||
|
||
def list_physical_column_names(cursor, table_name: str) -> list[str]:
|
||
cursor.execute(f"PRAGMA table_info({_quote_identifier(table_name)})")
|
||
rows = cursor.fetchall()
|
||
rows.sort(key=lambda row: int(row[0]))
|
||
return [str(row[1]) for row in rows]
|