签入版本
This commit is contained in:
@@ -0,0 +1,471 @@
|
||||
# +======================================================================
|
||||
# | DjangoAdmin 企业数字化开发平台 [ 赋能开发者,助力企业发展 ]
|
||||
# +----------------------------------------------------------------------
|
||||
# | Copyright (c) 2021-2026 小蚂蚁云团队 版权所有
|
||||
# +----------------------------------------------------------------------
|
||||
# | 开源协议: Apache-2.0
|
||||
# | 重要声明: 未经书面许可,不得移除或修改本框架相关版权标识。
|
||||
# +----------------------------------------------------------------------
|
||||
# | 官方网站: https://www.djangoadmin.cn
|
||||
# +----------------------------------------------------------------------
|
||||
# | 研发团队: 小蚂蚁云团队 荣誉出品
|
||||
# +======================================================================
|
||||
# | 版权和免责声明:
|
||||
# | 一、知识产权声明
|
||||
# | 本团队对该软件框架产品(以下简称“本产品”)拥有完整知识产权,包括但不限于商标权、专利权、
|
||||
# | 著作权、商业秘密等。前述权利均受中华人民共和国及相关国际法律法规保护。未经本团队书面授权,
|
||||
# | 任何个人或单位不得基于本产品本身申请任何形式的知识产权。
|
||||
# | 二、使用限制与禁止条款
|
||||
# | 您理解并同意,禁止将本产品用于以下任何行为:
|
||||
# | 1. 任何违反中华人民共和国法律法规的用途;
|
||||
# | 2. 任何侵害他人合法权益(包括但不限于人身权、财产权、知识产权等)的行为;
|
||||
# | 3. 任何危害国家安全、公共利益及社会道德风尚的行为。
|
||||
# | 三、责任豁免
|
||||
# | 因使用本产品而产生的任何损失或法律纠纷(包括但不限于直接损失、间接损失、附带损失、衍生损失
|
||||
# | 以及因违约、侵权等引发的责任),本团队均不承担任何法律责任。若因违规使用本产品导致任何第三
|
||||
# | 方提起索赔或行政机关进行调查,您应独立承担全部责任。
|
||||
# | 四、违约与法律责任
|
||||
# | 如发现任何违反本声明的行为,本团队有权:
|
||||
# | 1. 无条件配合公安机关及其他有权机关的调查取证工作;
|
||||
# | 2. 依法追究相关方的民事责任,并要求赔偿全部损失;
|
||||
# | 3. 保留进一步采取刑事控告等法律措施的权利。
|
||||
# | 五、许可范围说明
|
||||
# | 1、被授权方应视本产品源代码为核心机密信息,负有最高标准的保密与保管义务。禁止以任何形式(包
|
||||
# | 括但不限于直接或间接的分享、复制、传播、分发、转售、出租、开源、反向工程、反编译、反汇编或通
|
||||
# | 过网络服务提供给任何第三方)泄露、披露或处置源代码的全部或部分内容。任何未经授权的复制、使用、
|
||||
# | 传播行为均构成对本团队知识产权的严重侵权,本团队有权依法追究其全部法律责任,并要求赔偿因此造
|
||||
# | 成的一切经济损失及商誉损害。
|
||||
# | 2、本产品仅限于公司或个人在法律允许范围内,用于内部且合规的软件产品研发。具体权利义务以《软件
|
||||
# | 许可协议》附件为准。
|
||||
# +======================================================================
|
||||
|
||||
# +======================================================================
|
||||
# | 模块: 数据库初始化脚本
|
||||
# | 说明: 按 DB_DRIVER 分方言创建数据库并建表
|
||||
# +======================================================================
|
||||
|
||||
"""
|
||||
数据库初始化脚本
|
||||
=============================================================
|
||||
|
||||
按 DB_DRIVER 分方言创建数据库并建表:
|
||||
- mysql: 创建库(utf8mb4)→ 导入 document/mysql/djangoadmin.fastapi.naivevue.sql(若存在)
|
||||
→ 用模型 metadata 补建缺失的表
|
||||
- postgresql: 连 postgres 维护库,无则建库 → 导入 document/postgresql/ 下的 SQL(若存在)→ create_all
|
||||
- mssql: 连 master 库,无则建库 → 导入 document/sqlserver/ 下的 SQL(若存在)→ create_all
|
||||
- oracle: 连 service_name 校验连通(Oracle 无"建库"概念,schema 即连接用户,
|
||||
需 DBA 预建)→ create_all 建表 → 自动补 sequence + BEFORE INSERT
|
||||
触发器(Oracle create_all 不生成 Integer 主键自增)
|
||||
- sqlite: 直接 create_all 生成数据库文件(DB_DATABASE 即数据库文件完整路径)
|
||||
|
||||
SQL 脚本按数据库类型分目录存放在 document/ 下:
|
||||
document/mysql/djangoadmin.fastapi.naivevue.sql
|
||||
document/postgresql/djangoadmin.fastapi.naivevue.sql
|
||||
document/sqlserver/djangoadmin.fastapi.naivevue.sql
|
||||
document/sqlite/djangoadmin.fastapi.naivevue.sql
|
||||
document/oracle/djangoadmin.fastapi.naivevue.sql
|
||||
|
||||
用法:
|
||||
python scripts/init_db.py # 使用 .env 中的数据库配置
|
||||
python scripts/init_db.py --drop-first # 先删除已存在的同名库再重建
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "src"))
|
||||
|
||||
# Windows 控制台默认 GBK(cp936):把 stdout/stderr 切到 UTF-8,避免中文打印乱码/崩溃
|
||||
if sys.stdout and hasattr(sys.stdout, 'reconfigure'):
|
||||
sys.stdout.reconfigure(encoding='utf-8')
|
||||
sys.stderr.reconfigure(encoding='utf-8')
|
||||
|
||||
from sqlalchemy import text
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 辅助函数
|
||||
# ============================================================
|
||||
def _stamp_alembic():
|
||||
"""建库完成后将库标记到 alembic 基线,后续增量走迁移。失败不影响建库"""
|
||||
try:
|
||||
from alembic.config import Config
|
||||
from alembic import command
|
||||
|
||||
root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
cfg = Config(os.path.join(root, "alembic.ini"))
|
||||
command.stamp(cfg, "head")
|
||||
print("[INFO] Alembic 版本标记完成")
|
||||
except Exception as e:
|
||||
print(f"[WARN] Alembic 版本标记失败(可稍后手动执行 alembic stamp head): {e}")
|
||||
|
||||
|
||||
def split_statements(content: str):
|
||||
"""按 ';\\n' 切分语句,并剥离 -- 注释行,避免注释+语句整块被跳过"""
|
||||
for block in content.split(";\n"):
|
||||
body = []
|
||||
for line in block.splitlines():
|
||||
s = line.strip()
|
||||
if s.startswith("--") or s.startswith("#"):
|
||||
continue
|
||||
body.append(line)
|
||||
stmt = "\n".join(body).strip()
|
||||
if stmt:
|
||||
yield stmt
|
||||
|
||||
|
||||
def oracle_sequence_sql(table_name: str, start_with: int) -> str:
|
||||
"""Oracle:为无自增机制的 INTEGER 主键建 sequence"""
|
||||
return f"CREATE SEQUENCE {table_name}_id_seq START WITH {start_with} INCREMENT BY 1 NOCACHE"
|
||||
|
||||
|
||||
def oracle_trigger_sql(table_name: str) -> str:
|
||||
"""Oracle:BEFORE INSERT 触发器,id 为 NULL 时从 sequence 取 NEXTVAL"""
|
||||
seq = f"{table_name}_id_seq"
|
||||
trg = f"{table_name}_id_trg"
|
||||
return (
|
||||
f"CREATE OR REPLACE TRIGGER {trg}\n"
|
||||
f"BEFORE INSERT ON {table_name} FOR EACH ROW\n"
|
||||
f"WHEN (NEW.id IS NULL)\n"
|
||||
f"BEGIN SELECT {seq}.NEXTVAL INTO :NEW.id FROM dual; END;"
|
||||
)
|
||||
|
||||
|
||||
def ensure_oracle_autoincrement(conn, table_name: str):
|
||||
"""Oracle create_all 后补自增机制:重建 sequence + BEFORE INSERT 触发器"""
|
||||
max_id = conn.execute(text(f"SELECT COALESCE(MAX(id), 0) FROM {table_name}")).scalar()
|
||||
seq = f"{table_name}_id_seq"
|
||||
exists = conn.execute(text(
|
||||
"SELECT COUNT(*) FROM user_sequences WHERE sequence_name = UPPER(:n)"
|
||||
), {"n": seq}).scalar()
|
||||
if exists:
|
||||
conn.execute(text(f"DROP SEQUENCE {seq}"))
|
||||
conn.execute(text(oracle_sequence_sql(table_name, max_id + 1)))
|
||||
conn.execute(text(oracle_trigger_sql(table_name)))
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 主入口
|
||||
# ============================================================
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="初始化数据库")
|
||||
parser.add_argument("--drop-first", action="store_true", help="先删除已存在库再重建")
|
||||
args = parser.parse_args()
|
||||
|
||||
# 加载 .env 数据库配置
|
||||
from core.config import (
|
||||
DB_DRIVER, DB_HOST, DB_PORT, DB_DATABASE, DB_USERNAME, DB_PASSWORD,
|
||||
DB_PREFIX, SQLALCHEMY_DATABASE_URL,
|
||||
)
|
||||
|
||||
from sqlalchemy import create_engine, inspect
|
||||
from core.database import Base
|
||||
|
||||
# 加载全部模型(导入 create_app 会连带导入所有模块)
|
||||
from core.app import create_app
|
||||
|
||||
create_app()
|
||||
|
||||
# ----------------------------------------------------------
|
||||
# 启动日志
|
||||
# ----------------------------------------------------------
|
||||
print("[INFO] ============================================================")
|
||||
print("[INFO] 数据库初始化开始")
|
||||
print("[INFO] ============================================================")
|
||||
print(f"[INFO] 检测数据库驱动: {DB_DRIVER}")
|
||||
|
||||
# 驱动 → 可读名称
|
||||
driver_label = {
|
||||
'mysql': 'MySQL', 'postgresql': 'PostgreSQL',
|
||||
'mssql': 'SQL Server', 'oracle': 'Oracle', 'sqlite': 'SQLite',
|
||||
}
|
||||
|
||||
if DB_DRIVER != 'sqlite':
|
||||
print(f"[INFO] 连接 {driver_label.get(DB_DRIVER, DB_DRIVER)} 服务器: {DB_HOST}:{DB_PORT}")
|
||||
print(f"[INFO] 数据库配置: {DB_USERNAME}@{DB_HOST}:{DB_PORT}/{DB_DATABASE}")
|
||||
print(f"[INFO] 数据表前缀: {DB_PREFIX}")
|
||||
|
||||
# SQL 脚本路径:document/{driver}/djangoadmin.fastapi.naivevue.sql
|
||||
sql_filename = "djangoadmin.fastapi.naivevue.sql"
|
||||
driver_dir_map = {
|
||||
'mysql': 'mysql',
|
||||
'postgresql': 'postgresql',
|
||||
'mssql': 'sqlserver',
|
||||
'oracle': 'oracle',
|
||||
'sqlite': 'sqlite',
|
||||
}
|
||||
sql_path = os.path.join("document", driver_dir_map.get(DB_DRIVER, DB_DRIVER), sql_filename)
|
||||
|
||||
# ----------------------------------------------------------
|
||||
# 按驱动建库
|
||||
# ----------------------------------------------------------
|
||||
if DB_DRIVER == 'mysql':
|
||||
import pymysql
|
||||
|
||||
root_conn = pymysql.connect(host=DB_HOST, port=int(DB_PORT), user=DB_USERNAME, password=DB_PASSWORD)
|
||||
cur = root_conn.cursor()
|
||||
|
||||
# 检查数据库是否已存在
|
||||
cur.execute(f"SHOW DATABASES LIKE '{DB_DATABASE}'")
|
||||
db_exists = cur.fetchone() is not None
|
||||
|
||||
if args.drop_first and db_exists:
|
||||
print(f"[INFO] 删除已有数据库: {DB_DATABASE}")
|
||||
cur.execute(f"DROP DATABASE IF EXISTS `{DB_DATABASE}`")
|
||||
db_exists = False
|
||||
|
||||
if db_exists:
|
||||
print(f"[INFO] 数据库 {DB_DATABASE} 已存在,跳过创建")
|
||||
else:
|
||||
cur.execute(
|
||||
f"CREATE DATABASE IF NOT EXISTS `{DB_DATABASE}` "
|
||||
f"DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci"
|
||||
)
|
||||
print(f"[INFO] 创建数据库: {DB_DATABASE}")
|
||||
|
||||
cur.execute(f"USE `{DB_DATABASE}`")
|
||||
|
||||
# 导入 SQL 脚本
|
||||
if os.path.exists(sql_path):
|
||||
print(f"[INFO] 查找数据库脚本: {sql_path}")
|
||||
print("[INFO] 导入 SQL 脚本...")
|
||||
with open(sql_path, "r", encoding="utf-8") as f:
|
||||
content = f.read()
|
||||
done = errors = 0
|
||||
for stmt in split_statements(content):
|
||||
try:
|
||||
cur.execute(stmt)
|
||||
done += 1
|
||||
except Exception as e:
|
||||
errors += 1
|
||||
print(f"[SKIP] {e}\n {stmt[:100]}")
|
||||
root_conn.commit()
|
||||
print(f"[INFO] SQL 脚本导入完成: {done} 条语句")
|
||||
else:
|
||||
print(f"[WARN] 未找到 SQL 脚本: {sql_path},跳过导入")
|
||||
|
||||
cur.close()
|
||||
root_conn.close()
|
||||
|
||||
elif DB_DRIVER == 'postgresql':
|
||||
import psycopg
|
||||
|
||||
# 连 postgres 维护库创建目标库;DB_DATABASE 含点号,标识符必须双引号
|
||||
conn = psycopg.connect(
|
||||
host=DB_HOST, port=int(DB_PORT), user=DB_USERNAME,
|
||||
password=DB_PASSWORD, dbname="postgres", autocommit=True,
|
||||
)
|
||||
with conn.cursor() as cur:
|
||||
cur.execute("SELECT 1 FROM pg_database WHERE datname = %s", (DB_DATABASE,))
|
||||
exists = cur.fetchone() is not None
|
||||
|
||||
if args.drop_first and exists:
|
||||
print(f"[INFO] 删除已有数据库: {DB_DATABASE}")
|
||||
cur.execute(f'DROP DATABASE "{DB_DATABASE}" WITH (FORCE)')
|
||||
exists = False
|
||||
|
||||
if exists:
|
||||
print(f"[INFO] 数据库 {DB_DATABASE} 已存在,跳过创建")
|
||||
else:
|
||||
cur.execute(f'CREATE DATABASE "{DB_DATABASE}"')
|
||||
print(f"[INFO] 创建数据库: {DB_DATABASE}")
|
||||
conn.close()
|
||||
|
||||
# 导入 PostgreSQL SQL 脚本
|
||||
if os.path.exists(sql_path):
|
||||
print(f"[INFO] 查找数据库脚本: {sql_path}")
|
||||
print("[INFO] 导入 SQL 脚本...")
|
||||
import psycopg as pg
|
||||
pg_conn = pg.connect(
|
||||
host=DB_HOST, port=int(DB_PORT), user=DB_USERNAME,
|
||||
password=DB_PASSWORD, dbname=DB_DATABASE, autocommit=True,
|
||||
)
|
||||
with pg_conn.cursor() as cur:
|
||||
with open(sql_path, "r", encoding="utf-8") as f:
|
||||
cur.execute(f.read())
|
||||
pg_conn.close()
|
||||
print("[INFO] SQL 脚本导入完成")
|
||||
else:
|
||||
print(f"[WARN] 未找到 SQL 脚本: {sql_path},跳过导入")
|
||||
|
||||
elif DB_DRIVER == 'mssql':
|
||||
import pymssql
|
||||
|
||||
# 连 master 维护库创建目标库;标识符用方括号引用
|
||||
conn = pymssql.connect(
|
||||
server=DB_HOST, user=DB_USERNAME, password=DB_PASSWORD,
|
||||
port=int(DB_PORT), database="master", autocommit=True,
|
||||
)
|
||||
cur = conn.cursor()
|
||||
|
||||
# 检查数据库是否已存在
|
||||
cur.execute(f"SELECT DB_ID(N'{DB_DATABASE}')")
|
||||
db_exists = cur.fetchone()[0] is not None
|
||||
|
||||
if args.drop_first and db_exists:
|
||||
print(f"[INFO] 删除已有数据库: {DB_DATABASE}")
|
||||
cur.execute(
|
||||
f"ALTER DATABASE [{DB_DATABASE}] SET SINGLE_USER WITH ROLLBACK IMMEDIATE"
|
||||
)
|
||||
cur.execute(f"DROP DATABASE [{DB_DATABASE}]")
|
||||
db_exists = False
|
||||
|
||||
if db_exists:
|
||||
print(f"[INFO] 数据库 {DB_DATABASE} 已存在,跳过创建")
|
||||
else:
|
||||
cur.execute(f"CREATE DATABASE [{DB_DATABASE}]")
|
||||
print(f"[INFO] 创建数据库: {DB_DATABASE}")
|
||||
conn.close()
|
||||
|
||||
# 导入 SQL Server SQL 脚本
|
||||
if os.path.exists(sql_path):
|
||||
print(f"[INFO] 查找数据库脚本: {sql_path}")
|
||||
print("[INFO] 导入 SQL 脚本...")
|
||||
mssql_conn = pymssql.connect(
|
||||
server=DB_HOST, user=DB_USERNAME, password=DB_PASSWORD,
|
||||
port=int(DB_PORT), database=DB_DATABASE, autocommit=True,
|
||||
)
|
||||
mssql_cur = mssql_conn.cursor()
|
||||
with open(sql_path, "r", encoding="utf-8") as f:
|
||||
content = f.read()
|
||||
done = errors = 0
|
||||
for stmt in split_statements(content):
|
||||
try:
|
||||
mssql_cur.execute(stmt)
|
||||
done += 1
|
||||
except Exception as e:
|
||||
errors += 1
|
||||
print(f"[SKIP] {e}\n {stmt[:100]}")
|
||||
mssql_conn.commit()
|
||||
mssql_conn.close()
|
||||
print(f"[INFO] SQL 脚本导入完成: {done} 条语句")
|
||||
else:
|
||||
print(f"[WARN] 未找到 SQL 脚本: {sql_path},跳过导入")
|
||||
|
||||
elif DB_DRIVER == 'oracle':
|
||||
# Oracle 的"库"即连接的服务名(service_name),schema 是连接用户,由 DBA 预建,
|
||||
# 无"建库"概念;此处仅校验连通性,建表走下方 create_all
|
||||
import oracledb
|
||||
|
||||
conn = oracledb.connect(
|
||||
user=DB_USERNAME, password=DB_PASSWORD,
|
||||
dsn=f"{DB_HOST}:{DB_PORT}/{DB_DATABASE}",
|
||||
)
|
||||
conn.close()
|
||||
print(f"[INFO] Oracle 服务 {DB_DATABASE} 连通 (schema={DB_USERNAME})")
|
||||
|
||||
# 导入 Oracle SQL 脚本
|
||||
if os.path.exists(sql_path):
|
||||
print(f"[INFO] 查找数据库脚本: {sql_path}")
|
||||
print("[INFO] 导入 SQL 脚本...")
|
||||
ora_conn = oracledb.connect(
|
||||
user=DB_USERNAME, password=DB_PASSWORD,
|
||||
dsn=f"{DB_HOST}:{DB_PORT}/{DB_DATABASE}",
|
||||
)
|
||||
ora_cur = ora_conn.cursor()
|
||||
with open(sql_path, "r", encoding="utf-8") as f:
|
||||
content = f.read()
|
||||
done = errors = 0
|
||||
for stmt in split_statements(content):
|
||||
try:
|
||||
ora_cur.execute(stmt)
|
||||
done += 1
|
||||
except Exception as e:
|
||||
errors += 1
|
||||
print(f"[SKIP] {e}\n {stmt[:100]}")
|
||||
ora_conn.commit()
|
||||
ora_cur.close()
|
||||
ora_conn.close()
|
||||
print(f"[INFO] SQL 脚本导入完成: {done} 条语句")
|
||||
else:
|
||||
print(f"[WARN] 未找到 SQL 脚本: {sql_path},跳过导入")
|
||||
|
||||
else: # sqlite(DB_DATABASE 即数据库文件完整路径)
|
||||
from pathlib import Path
|
||||
|
||||
file = Path(DB_DATABASE).expanduser()
|
||||
print(f"[INFO] SQLite 数据库文件: {file}")
|
||||
|
||||
if file.parent.as_posix() != '.':
|
||||
file.parent.mkdir(parents=True, exist_ok=True)
|
||||
if args.drop_first and file.exists():
|
||||
file.unlink()
|
||||
print(f"[INFO] 删除已有数据库文件: {file}")
|
||||
|
||||
# 导入 SQLite SQL 脚本
|
||||
if os.path.exists(sql_path):
|
||||
print(f"[INFO] 查找数据库脚本: {sql_path}")
|
||||
print("[INFO] 导入 SQL 脚本...")
|
||||
import sqlite3 as sqlite_mod
|
||||
sq_conn = sqlite_mod.connect(str(file))
|
||||
with open(sql_path, "r", encoding="utf-8") as f:
|
||||
content = f.read()
|
||||
done = errors = 0
|
||||
for stmt in split_statements(content):
|
||||
try:
|
||||
sq_conn.execute(stmt)
|
||||
done += 1
|
||||
except Exception as e:
|
||||
errors += 1
|
||||
print(f"[SKIP] {e}\n {stmt[:100]}")
|
||||
sq_conn.commit()
|
||||
sq_conn.close()
|
||||
print(f"[INFO] SQL 脚本导入完成: {done} 条语句")
|
||||
else:
|
||||
print(f"[WARN] 未找到 SQL 脚本: {sql_path},跳过导入")
|
||||
|
||||
# ----------------------------------------------------------
|
||||
# 用模型补建缺失表
|
||||
# ----------------------------------------------------------
|
||||
print("[INFO] 创建数据表...")
|
||||
|
||||
engine = create_engine(SQLALCHEMY_DATABASE_URL)
|
||||
|
||||
# 记录 create_all 前已存在的表,用于差量统计新建的表
|
||||
existing_before = set(inspect(engine).get_table_names())
|
||||
|
||||
Base.metadata.create_all(engine)
|
||||
|
||||
# 差量:打印新建的表
|
||||
existing_after = set(inspect(engine).get_table_names())
|
||||
newly_created = sorted(existing_after - existing_before)
|
||||
if newly_created:
|
||||
for t in newly_created:
|
||||
print(f"[INFO] 表 {t} 创建成功")
|
||||
else:
|
||||
print("[INFO] 所有数据表均已存在,无需创建")
|
||||
|
||||
missing = set(Base.metadata.tables) - existing_after
|
||||
total_tables = len(existing_after)
|
||||
print(f"[INFO] 数据表创建完成: 共 {total_tables} 张表")
|
||||
if missing:
|
||||
print(f"[WARN] 以下表仍缺失: {sorted(missing)}")
|
||||
|
||||
# Oracle:create_all 对 Integer 主键只生成 INTEGER NOT NULL(无自增),
|
||||
# 补 sequence + BEFORE INSERT 触发器
|
||||
if DB_DRIVER == 'oracle':
|
||||
print("[INFO] 补建 Oracle 自增 sequence + 触发器...")
|
||||
with engine.begin() as conn:
|
||||
for table_name in Base.metadata.tables:
|
||||
ensure_oracle_autoincrement(conn, table_name)
|
||||
print(f"[INFO] 已为 {len(Base.metadata.tables)} 张表补建自增 sequence + 触发器")
|
||||
|
||||
engine.dispose()
|
||||
|
||||
# ----------------------------------------------------------
|
||||
# Alembic 版本标记
|
||||
# ----------------------------------------------------------
|
||||
print("[INFO] 标记 Alembic 版本...")
|
||||
_stamp_alembic()
|
||||
|
||||
# ----------------------------------------------------------
|
||||
# 完成
|
||||
# ----------------------------------------------------------
|
||||
print("[INFO] ============================================================")
|
||||
print("[INFO] 数据库初始化完成!")
|
||||
print("[INFO] ============================================================")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user