使用python 一键生成,PGSQL的数据字典

直接上代码

python 复制代码
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
PostgreSQL 数据字典生成器 (Python 3.11+)
生成完全离线的 HTML 文件,可直接双击在浏览器中打开。
"""

import psycopg2
import datetime
import os
import sys
from typing import List, Dict, Any

# 数据库配置(请根据实际情况修改)
DB_CONFIG = {
    "host": "127.0.0.1",
    "port": 5432,
    "database": "postgres",
    "user": "postgres",
    "password": "postgres"
}

def get_tables_and_comments(conn) -> List[Dict[str, str]]:
    """获取 public 模式下所有用户表及其注释"""
    with conn.cursor() as cur:
        cur.execute("""
            SELECT
                t.table_name,
                obj_description(c.oid, 'pg_class') AS table_comment
            FROM information_schema.tables t
            JOIN pg_class c ON c.relname = t.table_name
            WHERE t.table_schema = 'public'
              AND t.table_type = 'BASE TABLE'
            ORDER BY t.table_name;
        """)
        return [
            {"TABLE_NAME": row[0], "TABLE_COMMENT": row[1] or ""}
            for row in cur.fetchall()
        ]

def get_columns_info(conn, table_name: str, table_schema: str = 'public') -> List[Dict[str, Any]]:
    """获取指定表的所有列详细信息"""
    with conn.cursor() as cur:
        # 获取列注释(使用 pg_* 系统表查询)
        cur.execute("""
            SELECT a.attname, COALESCE(col_description(a.attrelid, a.attnum), '')
            FROM pg_catalog.pg_attribute a
            JOIN pg_catalog.pg_class c ON c.oid = a.attrelid
            JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
            WHERE n.nspname = %s AND c.relname = %s AND a.attnum > 0 AND NOT a.attisdropped
            ORDER BY a.attnum;
        """, (table_schema, table_name))
        comments = {row[0]: row[1] for row in cur.fetchall()}

        # 获取列基本信息
        cur.execute("""
            SELECT
                c.column_name,
                c.data_type,
                c.column_default,
                c.is_nullable,
                CASE
                    WHEN c.column_default LIKE 'nextval(%%' THEN TRUE
                    ELSE FALSE
                END AS is_auto_increment
            FROM information_schema.columns c
            WHERE c.table_schema = %s AND c.table_name = %s
            ORDER BY c.ordinal_position;
        """, (table_schema, table_name))

        columns = []
        for row in cur.fetchall():
            if row is None:
                continue
            col_name = row[0] if len(row) > 0 else ""
            columns.append({
                "COLUMN_NAME": col_name,
                "COLUMN_TYPE": row[1] if len(row) > 1 else "",
                "COLUMN_DEFAULT": row[2] if len(row) > 2 else "",
                "IS_NULLABLE": row[3] if len(row) > 3 else "NO",
                "IS_AUTO_INCREMENT": row[4] if len(row) > 4 else False,
                "COLUMN_COMMENT": comments.get(col_name, "")
            })
        return columns

def generate_offline_html(tables: List[Dict], db_name: str) -> str:
    """生成完整的离线 HTML 内容"""
    # 表清单 - 使用百分比宽度,自动适应页面
    table_list_html = '<table border="1" cellspacing="0" cellpadding="5" style="width:100%;table-layout:fixed;">\n'
    table_list_html += '<colgroup><col style="width:8%"><col style="width:25%"><col style="width:67%"></colgroup>\n'
    table_list_html += '<thead><tr><th>序号</th><th>表名</th><th>功能说明</th></tr></thead>\n<tbody>\n'
    for idx, tbl in enumerate(tables, 1):
        table_list_html += f'  <tr><td>{idx}</td><td>{tbl["TABLE_NAME"]}</td><td>{tbl["TABLE_COMMENT"]}</td></tr>\n'
    table_list_html += '</tbody>\n</table>\n'

    # 字段详情
    detail_html = ""
    for tbl in tables:
        detail_html += '<table border="1" cellspacing="0" cellpadding="5" style="width:100%;table-layout:fixed;">\n'
        detail_html += '<colgroup><col style="width:15%"><col style="width:12%"><col style="width:15%"><col style="width:8%"><col style="width:8%"><col style="width:42%"></colgroup>\n'
        comment_part = f' - {tbl["TABLE_COMMENT"]}' if tbl["TABLE_COMMENT"] else ""
        detail_html += f'  <caption><b>表名:{tbl["TABLE_NAME"]}{comment_part}</b></caption>\n'
        detail_html += '  <thead>\n    <tr><th>字段名</th><th>数据类型</th><th>默认值</th><th>允许非空</th><th>自动递增</th><th>备注</th></tr>\n  </thead>\n  <tbody>\n'
        for col in tbl.get("COLUMNS", []):
            nullable_display = "是" if col["IS_NULLABLE"] == "YES" else "否"
            auto_inc_display = "是" if col["IS_AUTO_INCREMENT"] else "-"
            detail_html += (
                f'    <tr>'
                f'<td>{col["COLUMN_NAME"]}</td>'
                f'<td>{col["COLUMN_TYPE"]}</td>'
                f'<td>{col["COLUMN_DEFAULT"]}</td>'
                f'<td>{nullable_display}</td>'
                f'<td>{auto_inc_display}</td>'
                f'<td>{col["COLUMN_COMMENT"]}</td>'
                f'</tr>\n'
            )
        detail_html += '  </tbody>\n</table>\n<br/>\n'

    # 完整 HTML
    now = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
    return f'''<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <title>{db_name} 数据字典</title>
    <style>
        body {{ font-family: "宋体", "SimSun", serif; font-size: 12pt; margin: 20px; background: white; color: black; }}
        h1 {{ text-align: center; color: black; margin-bottom: 15px; font-size: 16pt; }}
        .summary {{ text-align: center; color: #666666; margin-bottom: 20px; font-size: 11pt; }}
        table {{ border-collapse: collapse; margin-bottom: 20px; page-break-inside: avoid; word-wrap: break-word; }}
        caption {{ text-align: left; font-weight: bold; font-size: 12pt; color: black; padding: 6px 0; }}
        th, td {{ border: 1px solid #000000; padding: 5px 6px; text-align: left; vertical-align: top; font-size: 11pt; word-wrap: break-word; }}
        thead th {{ background-color: #E0E0E0; font-weight: bold; }}
        .footer {{ text-align: left; margin-top: 20px; color: #666666; font-size: 11pt; }}
    </style>
</head>
<body>
    <h1>{db_name} 数据字典</h1>
    <p class="summary">生成时间:{now}</p>
    {table_list_html}
    <br/>
    {detail_html}
    <p class="footer">总共:{len(tables)} 个数据表</p>
</body>
</html>'''

def main():
    try:
        print("🔍 正在连接 PostgreSQL 数据库...")
        with psycopg2.connect(**DB_CONFIG) as conn:
            tables = get_tables_and_comments(conn)
            print(f"✅ 成功获取 {len(tables)} 张表")

            total_cols = 0
            for tbl in tables:
                cols = get_columns_info(conn, tbl["TABLE_NAME"])
                tbl["COLUMNS"] = cols
                total_cols += len(cols)

            html_content = generate_offline_html(tables, DB_CONFIG["database"])

            # 生成带时间戳的文件名
            timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
            output_file = f'{DB_CONFIG["database"]}_数据字典_{timestamp}.html'

            with open(output_file, "w", encoding="utf-8") as f:
                f.write(html_content)

            abs_path = os.path.abspath(output_file)
            print(f"🎉 数据字典已生成!")
            print(f"📁 文件路径: {abs_path}")
            print(f"📊 共 {len(tables)} 张表,{total_cols} 个字段")
            print("\n💡 提示:双击该 HTML 文件即可在浏览器中查看!")

    except psycopg2.Error as e:
        print(f"❌ 数据库错误: {e}")
        sys.exit(1)
    except Exception as e:
        print(f"💥 程序异常: {e}")
        import traceback
        traceback.print_exc()
        sys.exit(1)

if __name__ == "__main__":
    main()

效果如下图所示:

相关推荐
高山流水&上善2 小时前
基于Qwen3-0.6B的光纤基础测试问答系统设计与实现
python·django
疯狂成瘾者2 小时前
PromptTemplate类解读
python·langchain
kk在加油2 小时前
python学习笔记(基础语法与变量、容器)
笔记·python·学习
橙露2 小时前
Python 正则表达式:文本清洗与信息提取速通手册
python·正则表达式·easyui
Lyyaoo.2 小时前
【JAVA基础面经】进程间的通信方式
java·开发语言·python
henrylin99992 小时前
Hermes Agent 06. 技能、记忆与上下文文件
人工智能·python·机器学习·hermes·hermesagent
Tisfy2 小时前
LeetCode 1848.到目标元素的最小距离:数组遍历(附python一行版)
python·leetcode·题解·遍历
这辈子谁会真的心疼你3 小时前
如何修改照片的拍摄信息?三个实用方案分享
java·python·数码相机
杜子不疼.3 小时前
Python 爬虫 + AI 总结:自动生成行业日报系统
人工智能·爬虫·python