소스 검색

迁移sqlite数据成功

flower_bs 1 개월 전
부모
커밋
2c45f09885
5개의 변경된 파일1368개의 추가작업 그리고 64개의 파일을 삭제
  1. 18 0
      container/migrations/0033_alter_containerwcsmodel_tasknumber.py
  2. 1 1
      container/models.py
  3. 19 19
      greaterwms/settings.py
  4. 125 44
      migrate_sqlite_to_postgres.py
  5. 1205 0
      migration_log_20250919_190948.txt

+ 18 - 0
container/migrations/0033_alter_containerwcsmodel_tasknumber.py

@@ -0,0 +1,18 @@
+# Generated by Django 4.1.2 on 2025-09-19 19:06
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('container', '0032_containerlistmodel_batch_info_and_more'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='containerwcsmodel',
+            name='tasknumber',
+            field=models.BigIntegerField(unique=True, verbose_name='任务号'),
+        ),
+    ]

+ 1 - 1
container/models.py

@@ -758,7 +758,7 @@ class ContainerWCSModel(models.Model):
     priority = models.IntegerField(default=100, verbose_name='优先级')
     month = models.IntegerField(verbose_name='月份')
     tasktype = models.CharField(max_length=50, verbose_name='任务类型')
-    tasknumber = models.IntegerField(verbose_name='任务号',unique=True)
+    tasknumber = models.BigIntegerField(verbose_name='任务号',unique=True)
     order_number = models.IntegerField(verbose_name='c_number')
     container = models.CharField(max_length=50, verbose_name='托盘号')
     current_location = models.CharField(max_length=50, verbose_name='当前库位')

+ 19 - 19
greaterwms/settings.py

@@ -86,30 +86,30 @@ CSRF_COOKIE_SAMESITE = None
 # Database
 # https://docs.djangoproject.com/en/3.1/ref/settings/#databases
 # update
-DATABASES = {
-    'default': {
-        'ENGINE': 'django.db.backends.sqlite3',
-        'NAME': BASE_DIR / 'db.sqlite3',
-        'OPTIONS': {
-            'timeout': 60,
-        }
-    }
-}
 # DATABASES = {
 #     'default': {
-#         'ENGINE': 'django.db.backends.postgresql',
-#         'NAME': 'wmsdb',        # 你的数据库名
-#         'USER': 'wmsuser',      # 创建的用户名
-#         'PASSWORD': 'abc@1234',  # 设置的密码
-#         'HOST': 'localhost',
-#         'PORT': '5432',
-#         # 以下为优化选项(可选但推荐)
+#         'ENGINE': 'django.db.backends.sqlite3',
+#         'NAME': BASE_DIR / 'db.sqlite3',
 #         'OPTIONS': {
-#             'connect_timeout': 60,   # 连接超时时间(秒)
-#         },
-#         'CONN_MAX_AGE': 300,       # 连接持久时间(秒)
+#             'timeout': 60,
+#         }
 #     }
 # }
+DATABASES = {
+    'default': {
+        'ENGINE': 'django.db.backends.postgresql',
+        'NAME': 'wmsdb',        # 你的数据库名
+        'USER': 'wmsuser',      # 创建的用户名
+        'PASSWORD': 'abc@1234',  # 设置的密码
+        'HOST': 'localhost',
+        'PORT': '5432',
+        # 以下为优化选项(可选但推荐)
+        'OPTIONS': {
+            'connect_timeout': 60,   # 连接超时时间(秒)
+        },
+        'CONN_MAX_AGE': 300,       # 连接持久时间(秒)
+    }
+}
 
 DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
 

+ 125 - 44
migrate_sqlite_to_postgres.py

@@ -2,6 +2,7 @@ import sqlite3
 import psycopg2
 import os
 import sys
+import datetime
 from dotenv import load_dotenv
 from psycopg2 import sql
 from collections import defaultdict
@@ -53,10 +54,25 @@ class SQLiteToPostgresMigrator:
         
         # 存储字段长度限制
         self.column_lengths = {}
+        
+        # 日志文件
+        self.log_file = None
+        self.log_file_path = f"migration_log_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.txt"
+
+    def log(self, message):
+        """记录日志到控制台和文件"""
+        print(message)
+        if self.log_file:
+            self.log_file.write(message + "\n")
+            self.log_file.flush()
 
     def connect(self):
         """连接到数据库"""
         try:
+            # 打开日志文件
+            self.log_file = open(self.log_file_path, "w", encoding="utf-8")
+            self.log(f"迁移日志文件: {self.log_file_path}")
+            
             # SQLite 连接
             self.sqlite_conn = sqlite3.connect(self.sqlite_db_path)
             self.sqlite_cursor = self.sqlite_conn.cursor()
@@ -64,10 +80,10 @@ class SQLiteToPostgresMigrator:
             # PostgreSQL 连接
             self.pg_conn = psycopg2.connect(**self.pg_config)
             self.pg_cursor = self.pg_conn.cursor()
-            print(f"连接成功: SQLite({self.sqlite_db_path}) → PostgreSQL({self.pg_config['dbname']})")
+            self.log(f"连接成功: SQLite({self.sqlite_db_path}) → PostgreSQL({self.pg_config['dbname']})")
             return True
         except Exception as e:
-            print(f"连接失败: {str(e)}")
+            self.log(f"连接失败: {str(e)}")
             return False
 
     def close(self):
@@ -76,14 +92,17 @@ class SQLiteToPostgresMigrator:
             if cursor: cursor.close()
         for conn in [self.sqlite_conn, self.pg_conn]:
             if conn: conn.close()
+        if self.log_file:
+            self.log_file.close()
+            self.log(f"日志文件已保存: {self.log_file_path}")
 
     def clear_postgres_data(self):
         """清空 PostgreSQL 数据库中的所有数据"""
-        print("\n警告: 此操作将清空 PostgreSQL 数据库中的所有数据!")
+        self.log("\n警告: 此操作将清空 PostgreSQL 数据库中的所有数据!")
         confirm = input("确定要清空 PostgreSQL 数据库吗? (y/n): ")
         
         if confirm.lower() != 'y':
-            print("取消清空操作")
+            self.log("取消清空操作")
             return False
         
         try:
@@ -99,17 +118,17 @@ class SQLiteToPostgresMigrator:
             for table in tables:
                 try:
                     self.pg_cursor.execute(f"TRUNCATE TABLE {table} CASCADE;")
-                    print(f"已清空表: {table}")
+                    self.log(f"已清空表: {table}")
                 except Exception as e:
-                    print(f"清空表 {table} 时出错: {str(e)}")
+                    self.log(f"清空表 {table} 时出错: {str(e)}")
                     continue
             
             self.pg_conn.commit()
-            print("成功清空 PostgreSQL 数据库中的所有数据")
+            self.log("成功清空 PostgreSQL 数据库中的所有数据")
             return True
         except Exception as e:
             self.pg_conn.rollback()
-            print(f"清空数据库时出错: {str(e)}")
+            self.log(f"清空数据库时出错: {str(e)}")
             return False
 
     def analyze_dependencies(self):
@@ -148,7 +167,7 @@ class SQLiteToPostgresMigrator:
                 visit(table)
         
         self.migration_order = order
-        print(f"分析完成: 发现 {len(tables)} 个表, 依赖顺序已确定")
+        self.log(f"分析完成: 发现 {len(tables)} 个表, 依赖顺序已确定")
 
     def get_pg_table_structure(self, table_name):
         """获取 PostgreSQL 表结构"""
@@ -216,10 +235,33 @@ class SQLiteToPostgresMigrator:
                 if len(value) > max_length:
                     # 截断超过长度的字符串
                     truncated = value[:max_length]
-                    print(f"  警告: 字段 '{col_name}' 值被截断 ({len(value)} -> {max_length})")
+                    self.log(f"  警告: 字段 '{col_name}' 值被截断 ({len(value)} -> {max_length})")
                     transformed.append(truncated)
                 else:
                     transformed.append(value)
+            # 处理整数范围限制
+            elif pg_type in ('integer', 'int', 'int4'):
+                # 检查值是否在PostgreSQL整数范围内
+                if isinstance(value, int) and (value < -2147483648 or value > 2147483647):
+                    # 尝试转换为bigint
+                    try:
+                        self.log(f"  警告: 字段 '{col_name}' 值超出整数范围 ({value}),尝试转换为bigint")
+                        transformed.append(value)
+                    except Exception:
+                        self.log(f"  错误: 无法转换字段 '{col_name}' 的值 ({value})")
+                        transformed.append(None)
+                else:
+                    transformed.append(value)
+            # 处理bigint范围限制
+            elif pg_type in ('bigint', 'int8'):
+                # 检查值是否在bigint范围内
+                bigint_min = -9223372036854775808
+                bigint_max = 9223372036854775807
+                if isinstance(value, int) and (value < bigint_min or value > bigint_max):
+                    self.log(f"  错误: 字段 '{col_name}' 值超出bigint范围 ({value})")
+                    transformed.append(None)
+                else:
+                    transformed.append(value)
             else:
                 transformed.append(value)
         return transformed
@@ -250,33 +292,67 @@ class SQLiteToPostgresMigrator:
         if not dependencies:
             return True
         
-        print(f"  检查 {table} 的依赖表迁移状态...")
+        self.log(f"  检查 {table} 的依赖表迁移状态...")
         all_migrated = True
         
         for dep_table in dependencies:
             if dep_table in self.migrated_tables:
-                print(f"    ✓ {dep_table} 已迁移")
+                self.log(f"    ✓ {dep_table} 已迁移")
             elif dep_table in self.failed_tables:
-                print(f"    ✗ {dep_table} 迁移失败")
+                self.log(f"    ✗ {dep_table} 迁移失败")
                 all_migrated = False
             else:
-                print(f"    ? {dep_table} 尚未迁移")
+                self.log(f"    ? {dep_table} 尚未迁移")
                 all_migrated = False
         
         return all_migrated
 
+    def handle_integer_overflow(self, table, columns, pg_structure, row):
+        """处理整数超出范围错误"""
+        # 找出导致问题的字段
+        for i, value in enumerate(row):
+            col_name = columns[i]
+            col_info = pg_structure.get(col_name.lower(), {})
+            pg_type = col_info.get('data_type')
+            
+            if pg_type in ('integer', 'int', 'int4') and isinstance(value, int):
+                if value < -2147483648 or value > 2147483647:
+                    self.log(f"  检测到字段 '{col_name}' 值超出范围 ({value})")
+                    
+                    # 尝试将字段类型改为bigint
+                    try:
+                        self.log(f"  尝试将字段 '{col_name}' 类型改为 bigint")
+                        alter_query = sql.SQL("""
+                            ALTER TABLE {} ALTER COLUMN {} TYPE BIGINT;
+                        """).format(
+                            sql.Identifier(table.lower()),
+                            sql.Identifier(col_name)
+                        )
+                        self.pg_cursor.execute(alter_query)
+                        self.pg_conn.commit()
+                        self.log(f"  成功将字段 '{col_name}' 类型改为 bigint")
+                        
+                        # 更新表结构信息
+                        pg_structure = self.get_pg_table_structure(table)
+                        return True
+                    except Exception as e:
+                        self.log(f"  修改字段类型失败: {str(e)}")
+                        self.pg_conn.rollback()
+                        return False
+        return False
+
     def migrate_table(self, table):
         """迁移单个表"""
-        print(f"\n准备迁移表: {table}")
+        self.log(f"\n准备迁移表: {table}")
         
         # 检查是否需要跳过
         if table in self.skip_tables:
-            print(f"  跳过系统表: {table}")
+            self.log(f"  跳过系统表: {table}")
             return True
         
         # 检查依赖表是否已迁移
         if not self.check_dependencies_migrated(table):
-            print(f"  依赖表未完全迁移,暂时跳过 {table}")
+            self.log(f"  依赖表未完全迁移,暂时跳过 {table}")
             return False
         
         # 获取 SQLite 表结构
@@ -292,7 +368,7 @@ class SQLiteToPostgresMigrator:
         rows = self.sqlite_cursor.fetchall()
         
         if not rows:
-            print(f"  表 {table} 为空,跳过")
+            self.log(f"  表 {table} 为空,跳过")
             self.migrated_tables.add(table)
             return True
         
@@ -332,22 +408,22 @@ class SQLiteToPostgresMigrator:
             )
         
         # 用户确认
-        print(f"  表 {table} 有 {len(rows)} 行数据需要迁移")
+        self.log(f"  表 {table} 有 {len(rows)} 行数据需要迁移")
         if primary_keys:
-            print(f"  使用主键 ({', '.join(primary_keys)}) 处理冲突")
+            self.log(f"  使用主键 ({', '.join(primary_keys)}) 处理冲突")
         else:
-            print("  警告: 表没有主键,可能产生重复数据")
+            self.log("  警告: 表没有主键,可能产生重复数据")
         
         # 显示字段长度限制
         if column_lengths:
-            print("  字段长度限制:")
+            self.log("  字段长度限制:")
             for col, max_len in column_lengths.items():
-                print(f"    {col}: {max_len} 字符")
+                self.log(f"    {col}: {max_len} 字符")
         
         confirm = input("  按 Enter 键开始迁移,或输入 's' 跳过此表: ")
         
         if confirm.lower() == 's':
-            print(f"  已跳过表 {table}")
+            self.log(f"  已跳过表 {table}")
             return True
         
         # 迁移数据
@@ -363,28 +439,33 @@ class SQLiteToPostgresMigrator:
                 except psycopg2.IntegrityError as e:
                     error_count += 1
                     if 'foreign key constraint' in str(e):
-                        print(f"  外键约束错误: {str(e)}")
+                        self.log(f"  外键约束错误: {str(e)}")
+                    elif 'integer out of range' in str(e):
+                        # 处理整数超出范围错误
+                        self.log(f"  整数超出范围错误: {str(e)}")
+                        # 尝试将字段类型改为bigint
+                        self.handle_integer_overflow(table, columns, pg_structure, row)
                     else:
-                        print(f"  完整性错误: {str(e)}")
+                        self.log(f"  完整性错误: {str(e)}")
                     self.pg_conn.rollback()
                 except Exception as e:
                     error_count += 1
-                    print(f"  行插入失败: {str(e)}")
+                    self.log(f"  行插入失败: {str(e)}")
                     self.pg_conn.rollback()
             
             self.pg_conn.commit()
             
             if error_count == 0:
-                print(f"  成功迁移 {success_count} 行数据")
+                self.log(f"  成功迁移 {success_count} 行数据")
                 self.migrated_tables.add(table)
                 return True
             else:
-                print(f"  部分迁移: {success_count} 行成功, {error_count} 行失败")
+                self.log(f"  部分迁移: {success_count} 行成功, {error_count} 行失败")
                 self.failed_tables.add(table)
                 return False
         except Exception as e:
             self.pg_conn.rollback()
-            print(f"  迁移表 {table} 时出错: {str(e)}")
+            self.log(f"  迁移表 {table} 时出错: {str(e)}")
             self.failed_tables.add(table)
             return False
 
@@ -395,24 +476,24 @@ class SQLiteToPostgresMigrator:
         
         try:
             # 询问是否清空 PostgreSQL 数据
-            print("\n是否在迁移前清空 PostgreSQL 数据库?")
-            print("1. 清空所有数据 (确保无重复)")
-            print("2. 不清空 (可能产生重复数据)")
-            print("3. 退出")
+            self.log("\n是否在迁移前清空 PostgreSQL 数据库?")
+            self.log("1. 清空所有数据 (确保无重复)")
+            self.log("2. 不清空 (可能产生重复数据)")
+            self.log("3. 退出")
             
             choice = input("请选择 (1/2/3): ")
             
             if choice == '3':
-                print("退出迁移")
+                self.log("退出迁移")
                 return False
                 
             if choice == '1':
                 if not self.clear_postgres_data():
-                    print("清空操作失败,退出迁移")
+                    self.log("清空操作失败,退出迁移")
                     return False
             
             self.analyze_dependencies()
-            print(f"\n开始迁移 {len(self.migration_order)} 个表")
+            self.log(f"\n开始迁移 {len(self.migration_order)} 个表")
             
             # 按依赖顺序迁移表
             remaining_tables = self.migration_order.copy()
@@ -421,7 +502,7 @@ class SQLiteToPostgresMigrator:
             
             while remaining_tables and attempt < max_attempts:
                 attempt += 1
-                print(f"\n第 {attempt} 轮迁移尝试")
+                self.log(f"\n第 {attempt} 轮迁移尝试")
                 
                 # 复制列表以便在迭代时修改
                 tables_to_migrate = remaining_tables.copy()
@@ -433,23 +514,23 @@ class SQLiteToPostgresMigrator:
                         remaining_tables.append(table)
                 
                 if remaining_tables:
-                    print(f"\n本轮迁移后仍有 {len(remaining_tables)} 个表未成功迁移")
+                    self.log(f"\n本轮迁移后仍有 {len(remaining_tables)} 个表未成功迁移")
             
             # 检查迁移结果
             if remaining_tables:
-                print(f"\n迁移完成,但以下表迁移失败:")
+                self.log(f"\n迁移完成,但以下表迁移失败:")
                 for table in remaining_tables:
-                    print(f"  - {table}")
+                    self.log(f"  - {table}")
             else:
-                print("\n所有表迁移成功!")
+                self.log("\n所有表迁移成功!")
             
             return True
         except Exception as e:
-            print(f"迁移过程中发生错误: {str(e)}")
+            self.log(f"迁移过程中发生错误: {str(e)}")
             return False
         finally:
             self.close()
 
 if __name__ == "__main__":
     migrator = SQLiteToPostgresMigrator()
-    migrator.migrate()
+    migrator.migrate()

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 1205 - 0
migration_log_20250919_190948.txt