Django模型删除字段或表时保留已有数据及新增unique字段问题咨询
Django 生产环境模型变更数据兼容方案
场景1:删除已上线存有待保留数据的字段column1
生产环境删除字段前必须先完成数据归档,禁止直接删除字段导致数据丢失,操作步骤如下:
- 前置操作:全量备份现有生产数据库,确认备份可正常恢复
- 步骤1:新增归档模型存储待删除字段的历史数据,示例模型定义如下:
from django.db import models from your_app.models import YourOriginalModel class ArchivedColumn1Data(models.Model): original_id = models.IntegerField(verbose_name="原记录ID", db_index=True) column1 = models.CharField(verbose_name="归档的column1值", max_length=255) # 字段类型和原column1保持一致 archive_time = models.DateTimeField(verbose_name="归档时间", auto_now_add=True) class Meta: db_table = "archived_column1_data"
- 步骤2:执行
python manage.py makemigrations --empty your_app_name生成空迁移文件,在迁移文件中添加数据归档逻辑,示例代码如下:
from django.db import migrations def archive_column1(apps, schema_editor): YourOriginalModel = apps.get_model('your_app_name', 'YourOriginalModel') ArchivedColumn1Data = apps.get_model('your_app_name', 'ArchivedColumn1Data') # 批量归档数据,数据量大时分页处理避免OOM batch_size = 1000 offset = 0 while True: records = YourOriginalModel.objects.all()[offset:offset+batch_size] if not records: break archive_objs = [ ArchivedColumn1Data(original_id=record.id, column1=record.column1) for record in records ] ArchivedColumn1Data.objects.bulk_create(archive_objs) offset += batch_size class Migration(migrations.Migration): dependencies = [ ('your_app_name', '000x_prev_migration_id'), # 替换为上一个迁移文件的编号 ] operations = [ migrations.RunPython(archive_column1), ]
- 步骤3:执行
python manage.py migrate完成数据归档,校验归档表数据和原表数据一致性 - 步骤4:删除原模型中的column1字段,生成新的迁移文件执行即可,后续需要历史数据时直接查询归档表
如果暂时不确定是否完全不需要该字段,也可以仅将原字段的editable属性设为False,并在admin、业务接口中隐藏该字段,不执行删除操作,数据会直接保留在原表中。
场景2:存量数据存在时新增带unique约束、不允许为null的字段column2
直接添加非空唯一字段会触发迁移报错,需要分三步完成变更:
- 前置操作:全量备份现有生产数据库,确认备份可正常恢复
- 步骤1:先给column2设置
null=True,暂不添加unique和非空约束,示例模型定义:
class YourOriginalModel(models.Model): # 原有其他字段 column2 = models.CharField(verbose_name="新字段", max_length=64, null=True)
生成迁移文件后执行,此时存量数据的column2值均为null
- 步骤2:根据业务规则为所有存量数据生成唯一非空的column2值,可通过自定义迁移实现批量填充,示例逻辑如下:
from django.db import migrations def fill_column2(apps, schema_editor): YourOriginalModel = apps.get_model('your_app_name', 'YourOriginalModel') batch_size = 1000 offset = 0 while True: records = YourOriginalModel.objects.filter(column2__isnull=True)[offset:offset+batch_size] if not records: break for record in records: # 替换为业务侧的唯一值生成规则,示例为前缀+主键ID record.column2 = f"BIZ_RULE_PREFIX_{record.id}" YourOriginalModel.objects.bulk_update(records, ['column2']) offset += batch_size class Migration(migrations.Migration): dependencies = [ ('your_app_name', '000x_add_nullable_column2'), # 替换为上一步添加column2的迁移编号 ] operations = [ migrations.RunPython(fill_column2), ]
执行迁移后校验所有存量数据的column2值均非空且无重复
- 步骤3:修改column2的模型定义,去掉
null=True,添加unique=True:
class YourOriginalModel(models.Model): # 原有其他字段 column2 = models.CharField(verbose_name="新字段", max_length=64, unique=True)
生成迁移文件后执行即可。后续新增记录可通过重写save方法、pre_save信号或者业务层逻辑自动生成符合规则的唯一值,避免插入报错。
内容的提问来源于stack exchange,提问作者Dhia Shalabi
相关产品推荐
相关产品推荐

