如何将Django的Post模型对象/查询集迁移至KeepPost并执行删除
Django 2.0.2 + Python 3.4 数据迁移与账号删除实现方案
嘿,我来帮你搞定这个Django数据迁移和账号删除的业务需求,先把基础信息和实现方案理清楚:
基础环境
- Python 3.4
- Django 2.0.2
现有模型定义(省略部分细节)
class Post(models.Model): postuid = models.BigAutoField(db_column='postUID', primary_key=True) useruid = models.ForeignKey(....) content = models.Text(...) registerdate = models.Date(...) class KeepPost(models.Model): keeppostuid = models.BigAutoField(db_column='KeepPostUID', primary_key=True) useruid = models.ForeignKey('Userinfo', db_column='UserUID', on_delete=models.CASCADE) postuid = models.ForeignKey('Post', db_column='PostUID', on_delete=models.DO_NOTHING) content = models.TextField(db_column='Content') registerdate = models.DateTimeField(db_column='RegisterDate') keepdate = models.DateTimeField(db_column='KeepDate') class ReportPost(models.Model): reportuid=models.BigAutoField(db_column='ReportUID', primary_key=True) postuid=models.ForeignKey('Post', db_column='PostUID', on_delete=models.DO_NOTHING) #useruid为举报者 useruid=models.ForeignKey('Userinfo', db_column='UserUID', on_delete=models.CASCADE) registerdate = models.DateTimeField(db_column='RegisterDate')
需求实现方案
1. 迁移单个Post对象/查询集到KeepPost并删除原数据
我写了一个通用函数,既能处理单个Post对象,也能处理filter返回的查询集,同时用批量创建提升数据库操作效率:
from django.utils import timezone from .models import Post, KeepPost def migrate_posts_to_keep(posts): # 处理单个Post对象的情况,转成可迭代的列表 if isinstance(posts, Post): posts = [posts] # 批量构建KeepPost实例 keep_post_instances = [] for post in posts: # 注意Post的registerdate是Date类型,需要转成DateTime适配KeepPost的字段 post_register_datetime = timezone.make_aware( timezone.datetime.combine(post.registerdate, timezone.datetime.min.time()) ) keep_post = KeepPost( useruid=post.useruid, postuid=post, content=post.content, registerdate=post_register_datetime, keepdate=timezone.now() # 记录迁移时间 ) keep_post_instances.append(keep_post) # 批量插入数据库,比逐条save更高效 KeepPost.objects.bulk_create(keep_post_instances) # 删除原Post数据 Post.objects.filter(postuid__in=[p.postuid for p in posts]).delete()
使用方式:
- 迁移单个对象:
migrate_posts_to_keep(Post.objects.get(postuid=1)) - 迁移查询集:
migrate_posts_to_keep(Post.objects.filter(useruid=1))
2. 用户删除账号时迁移被举报的Post再删除账号
这里需要先筛选出该用户发布的、存在举报记录的Post,迁移到KeepPost后再删除用户账号。为了避免中途出错导致数据不一致,建议用事务包裹整个操作:
from django.db import transaction from django.db.models import Exists, OuterRef from .models import Userinfo, Post, ReportPost @transaction.atomic def delete_user_with_reported_post_migration(user_id): # 获取要删除的用户对象 user = Userinfo.objects.get(pk=user_id) # 筛选该用户发布的、被举报过的Post(用Exists子查询高效判断) reported_posts = Post.objects.filter( useruid=user, Exists(ReportPost.objects.filter(postuid=OuterRef('postuid'))) ) # 先迁移这些被举报的Post到KeepPost migrate_posts_to_keep(reported_posts) # 执行用户删除 user.delete()
说明:
- 用
Exists子查询可以避免N+1查询,比__in更高效,尤其当数据量较大时 @transaction.atomic装饰器保证整个操作要么全部完成,要么全部回滚,避免数据异常
内容的提问来源于stack exchange,提问作者Adam
相关产品推荐
相关产品推荐

