migrate_add_soft_delete.py 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. """
  2. 数据库迁移脚本:为图像翻译表添加软删除字段
  3. 执行方式:
  4. python scripts/migrate_add_soft_delete.py
  5. 功能:
  6. 1. 添加 is_deleted 字段(Boolean,默认 False)
  7. 2. 添加 deleted_at 字段(DateTime,可为空)
  8. 3. 创建 is_deleted 索引
  9. """
  10. import sys
  11. import os
  12. # 添加项目根目录到 Python 路径
  13. sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
  14. from sqlalchemy import text
  15. from app.database import engine, SessionLocal
  16. def run_migration():
  17. """执行数据库迁移"""
  18. print("=" * 60)
  19. print("开始执行迁移:为图像翻译表添加软删除字段")
  20. print("=" * 60)
  21. db = SessionLocal()
  22. try:
  23. # 1. 添加 is_deleted 字段
  24. print("\n[1/4] 添加 is_deleted 字段...")
  25. try:
  26. db.execute(text("""
  27. ALTER TABLE aigcspace.image_translation
  28. ADD COLUMN IF NOT EXISTS is_deleted BOOLEAN NOT NULL DEFAULT FALSE
  29. """))
  30. db.commit()
  31. print("✓ is_deleted 字段添加成功")
  32. except Exception as e:
  33. print(f"⚠ is_deleted 字段可能已存在: {e}")
  34. db.rollback()
  35. # 2. 添加 deleted_at 字段
  36. print("\n[2/4] 添加 deleted_at 字段...")
  37. try:
  38. db.execute(text("""
  39. ALTER TABLE aigcspace.image_translation
  40. ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMP
  41. """))
  42. db.commit()
  43. print("✓ deleted_at 字段添加成功")
  44. except Exception as e:
  45. print(f"⚠ deleted_at 字段可能已存在: {e}")
  46. db.rollback()
  47. # 3. 创建索引
  48. print("\n[3/4] 创建 is_deleted 索引...")
  49. try:
  50. db.execute(text("""
  51. CREATE INDEX IF NOT EXISTS idx_image_translation_is_deleted
  52. ON aigcspace.image_translation(is_deleted)
  53. """))
  54. db.commit()
  55. print("✓ 索引创建成功")
  56. except Exception as e:
  57. print(f"⚠ 索引可能已存在: {e}")
  58. db.rollback()
  59. # 4. 添加字段注释
  60. print("\n[4/4] 添加字段注释...")
  61. try:
  62. db.execute(text("""
  63. COMMENT ON COLUMN aigcspace.image_translation.is_deleted IS '是否已删除'
  64. """))
  65. db.execute(text("""
  66. COMMENT ON COLUMN aigcspace.image_translation.deleted_at IS '删除时间'
  67. """))
  68. db.commit()
  69. print("✓ 字段注释添加成功")
  70. except Exception as e:
  71. print(f"⚠ 添加注释失败: {e}")
  72. db.rollback()
  73. # 验证迁移结果
  74. print("\n" + "=" * 60)
  75. print("验证迁移结果...")
  76. print("=" * 60)
  77. result = db.execute(text("""
  78. SELECT
  79. column_name,
  80. data_type,
  81. is_nullable,
  82. column_default
  83. FROM information_schema.columns
  84. WHERE table_schema = 'aigcspace'
  85. AND table_name = 'image_translation'
  86. AND column_name IN ('is_deleted', 'deleted_at')
  87. ORDER BY column_name
  88. """))
  89. columns = result.fetchall()
  90. if columns:
  91. print("\n新增字段信息:")
  92. for col in columns:
  93. print(f" - {col[0]}: {col[1]} (nullable: {col[2]}, default: {col[3]})")
  94. else:
  95. print("⚠ 警告:未找到新增字段,请检查迁移是否成功")
  96. # 检查索引
  97. result = db.execute(text("""
  98. SELECT indexname
  99. FROM pg_indexes
  100. WHERE schemaname = 'aigcspace'
  101. AND tablename = 'image_translation'
  102. AND indexname = 'idx_image_translation_is_deleted'
  103. """))
  104. index = result.fetchone()
  105. if index:
  106. print(f"\n索引信息:")
  107. print(f" - {index[0]}")
  108. else:
  109. print("\n⚠ 警告:未找到索引,请检查创建是否成功")
  110. # 统计现有记录
  111. result = db.execute(text("""
  112. SELECT COUNT(*) as total
  113. FROM aigcspace.image_translation
  114. """))
  115. total = result.fetchone()[0]
  116. print(f"\n当前表中记录总数: {total}")
  117. print(f"所有现有记录的 is_deleted 默认值为: FALSE")
  118. print("\n" + "=" * 60)
  119. print("✓ 迁移执行成功!")
  120. print("=" * 60)
  121. return True
  122. except Exception as e:
  123. print(f"\n✗ 迁移执行失败: {e}")
  124. import traceback
  125. traceback.print_exc()
  126. db.rollback()
  127. return False
  128. finally:
  129. db.close()
  130. def rollback_migration():
  131. """回滚迁移(仅用于测试环境)"""
  132. print("\n" + "=" * 60)
  133. print("警告:准备回滚迁移")
  134. print("=" * 60)
  135. confirm = input("\n确定要回滚迁移吗?这将删除 is_deleted 和 deleted_at 字段!(yes/no): ")
  136. if confirm.lower() != 'yes':
  137. print("已取消回滚")
  138. return
  139. db = SessionLocal()
  140. try:
  141. # 删除索引
  142. print("\n[1/3] 删除索引...")
  143. db.execute(text("""
  144. DROP INDEX IF EXISTS aigcspace.idx_image_translation_is_deleted
  145. """))
  146. db.commit()
  147. print("✓ 索引删除成功")
  148. # 删除字段
  149. print("\n[2/3] 删除 is_deleted 字段...")
  150. db.execute(text("""
  151. ALTER TABLE aigcspace.image_translation
  152. DROP COLUMN IF EXISTS is_deleted
  153. """))
  154. db.commit()
  155. print("✓ is_deleted 字段删除成功")
  156. print("\n[3/3] 删除 deleted_at 字段...")
  157. db.execute(text("""
  158. ALTER TABLE aigcspace.image_translation
  159. DROP COLUMN IF EXISTS deleted_at
  160. """))
  161. db.commit()
  162. print("✓ deleted_at 字段删除成功")
  163. print("\n✓ 回滚完成")
  164. except Exception as e:
  165. print(f"\n✗ 回滚失败: {e}")
  166. import traceback
  167. traceback.print_exc()
  168. db.rollback()
  169. finally:
  170. db.close()
  171. if __name__ == "__main__":
  172. import argparse
  173. parser = argparse.ArgumentParser(description='图像翻译表软删除字段迁移脚本')
  174. parser.add_argument(
  175. '--rollback',
  176. action='store_true',
  177. help='回滚迁移(仅用于测试环境)'
  178. )
  179. args = parser.parse_args()
  180. if args.rollback:
  181. rollback_migration()
  182. else:
  183. success = run_migration()
  184. sys.exit(0 if success else 1)