Django migration 新增外键的坑

2024-01-19 21:20
文章标签 django 新增 外键 migration

本文主要是介绍Django migration 新增外键的坑,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

TL;DR

永远不要相信 makemigrations!

migrate 之前一定好好看看 migrate 了啥东西,必要时手动修改生成的 migrate 文件。

最好把db的更新与服务代码更新解耦

场景

先描述下场景:

现在有两个表,一个是 question,一个是 choice,其中 question 和 choice 是一对多的关系,其中 choice 表中会记录 question_id(此时不是外键约束)。

class Question(models.Model):content = models.CharField(max_length=256, blank=True, default='')class Choice(models.Model):content = models.CharField(max_length=256, default='')question_id = models.IntegerField(null=True)

现在我想把 question_id 改为外键,所以很自然的改写 model:

class Question(models.Model):content = models.CharField(max_length=256, blank=True, default='')class Choice(models.Model):content = models.CharField(max_length=256, default='')question = models.ForeignKey(Question, on_delete=models.DO_NOTHING) 

然后快乐地执行 makemigration -> migrate,然后 choice 里面的 question_id 就全没了(实际上我还手贱加了个 default,导致都关联到 default 的 question 上了),然后第二天因为左脚先进办公室被开了。。。

原因

在自己的 demo 上复现了下。

问题出在 makemigrate 生成的文件上,解析出来的 operations 是先把 question_id 列 remove 掉(RemoveField),然后再加上(AddField)。。。

这样设计的思路我不是很懂,可能是有些 engine(比如SQLite)不支持对现有的表加外键?

解决

修改 migration 文件,把先 remove 再 add 的逻辑调整一下。

需要用到 migration.RunPython,migration 文件应该长成下边这样:

from django.db import migrations, models
import django.db.models.deletiondef duplicate_question_id(apps, schema_editor):Choice = apps.get_model("polls", "Choice")for c in Choice.objects.all():Question = apps.get_model("polls", "Question")q = Question.objects.get(id=c.question_id)c.question_cpy = qc.save()class Migration(migrations.Migration):# 一些必要的依赖,这里大概率不用改dependencies = []operations = [migrations.AddField(model_name="choice",name="question_cpy",# question_cpy 先把 question_id 列的数据 copy 过来field=models.ForeignKey(null=True,on_delete=django.db.models.deletion.DO_NOTHING,to="polls.question",),),migrations.RunPython(code=duplicate_question_id,reverse_code=migrations.RunPython.noop,# reverse_code 是 migrate 回退时会执行的操作# noop 方法是为了支持回退的空方法),migrations.RemoveField(model_name="choice",name="question_id",),migrations.RenameField(model_name="choice",old_name="question_cpy",new_name="question",),]

注意:你需要根据实际情况编写合适的代码,上面仅提供思路。

如果你是在sqlite上测试,可以正常 migrate,但是外键不会生效(因为 sqlite 默认关闭外键)。

如果在 mysql 上测试,应该是可以成功的(待验证)。

除了 RunPython 之外还可以用RunSQL,然后仔细确定SQL的合法性,个人感觉会更安全一些,毕竟直接操作 DB 的部分,普遍会认为潜在风险较高,会对相应动作有更高的敏感度。(不过最关键的还是要感知 makemigrations 其实不是个靠谱的东西)

避免

永远不要相信 makemigrations!

最好把db的更新与服务代码更新解耦

这篇关于Django migration 新增外键的坑的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/623731

相关文章

Django开发时如何避免频繁发送短信验证码(python图文代码)

《Django开发时如何避免频繁发送短信验证码(python图文代码)》Django开发时,为防止频繁发送验证码,后端需用Redis限制请求频率,结合管道技术提升效率,通过生产者消费者模式解耦业务逻辑... 目录避免频繁发送 验证码1. www.chinasem.cn避免频繁发送 验证码逻辑分析2. 避免频繁

Mysql实现范围分区表(新增、删除、重组、查看)

《Mysql实现范围分区表(新增、删除、重组、查看)》MySQL分区表的四种类型(范围、哈希、列表、键值),主要介绍了范围分区的创建、查询、添加、删除及重组织操作,具有一定的参考价值,感兴趣的可以了解... 目录一、mysql分区表分类二、范围分区(Range Partitioning1、新建分区表:2、分

MySQL 定时新增分区的实现示例

《MySQL定时新增分区的实现示例》本文主要介绍了通过存储过程和定时任务实现MySQL分区的自动创建,解决大数据量下手动维护的繁琐问题,具有一定的参考价值,感兴趣的可以了解一下... mysql创建好分区之后,有时候会需要自动创建分区。比如,一些表数据量非常大,有些数据是热点数据,按照日期分区MululbU

SQL 外键Foreign Key全解析

《SQL外键ForeignKey全解析》外键是数据库表中的一列(或一组列),用于​​建立两个表之间的关联关系​​,外键的值必须匹配另一个表的主键(PrimaryKey)或唯一约束(UniqueCo... 目录1. 什么是外键?​​ ​​​​2. 外键的语法​​​​3. 外键的约束行为​​​​4. 多列外键​

对Django中时区的解读

《对Django中时区的解读》:本文主要介绍对Django中时区的解读方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录背景前端数据库中存储接口返回AI的解释问题:这样设置的作用答案获取当前时间(自动带时区)转换为北京时间显示总结背景设置时区为北京时间 TIM

Django之定时任务django-crontab的实现

《Django之定时任务django-crontab的实现》Django可以使用第三方库如django-crontab来实现定时任务的调度,本文主要介绍了Django之定时任务django-cront... 目录crontab安装django-crontab注册应用定时时间格式定时时间示例设置定时任务@符号

mysql的基础语句和外键查询及其语句详解(推荐)

《mysql的基础语句和外键查询及其语句详解(推荐)》:本文主要介绍mysql的基础语句和外键查询及其语句详解(推荐),本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋... 目录一、mysql 基础语句1. 数据库操作 创建数据库2. 表操作 创建表3. CRUD 操作二、外键

MySQL新增字段后Java实体未更新的潜在问题与解决方案

《MySQL新增字段后Java实体未更新的潜在问题与解决方案》在Java+MySQL的开发中,我们通常使用ORM框架来映射数据库表与Java对象,但有时候,数据库表结构变更(如新增字段)后,开发人员可... 目录引言1. 问题背景:数据库与 Java 实体不同步1.1 常见场景1.2 示例代码2. 不同操作

Django序列化中SerializerMethodField的使用详解

《Django序列化中SerializerMethodField的使用详解》:本文主要介绍Django序列化中SerializerMethodField的使用,具有很好的参考价值,希望对大家有所帮... 目录SerializerMethodField的基本概念使用SerializerMethodField的

mysql外键创建不成功/失效如何处理

《mysql外键创建不成功/失效如何处理》文章介绍了在MySQL5.5.40版本中,创建带有外键约束的`stu`和`grade`表时遇到的问题,发现`grade`表的`id`字段没有随着`studen... 当前mysql版本:SELECT VERSION();结果为:5.5.40。在复习mysql外键约