You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django调用Model.objects.create创建实例后主键为None如何获取?

问题原因

你遇到的问题核心是Django对不同类型主键的处理逻辑不同:

  • 当你不手动指定主键时,Django默认会为模型添加AutoField(primary_key=True)类型的id字段,这类字段属于自增字段,Django在执行create操作入库后,会自动将数据库生成的自增ID回写到返回的实例对象上。
  • 当你手动指定IntegerField(primary_key=True)作为主键时,Django默认会认为该字段的值由你自己主动提供,不会将其识别为自增字段,因此数据库插入完成后不会主动把数据库生成的自增ID同步到实例对象上,才会出现实例id为None、但数据库中已经有对应ID的情况。
  • 你数据库中能看到生成的ID,大概率是因为之前用默认AutoField生成的表结构已经给id字段加上了自增属性,后续你改成IntegerField后,迁移操作不会修改原有字段的自增属性,所以数据库插入时还是会自动生成ID,只是Django层面没有做回写。
    另外你的测试代码还存在一个基础问题:setUp方法中创建的学生实例没有赋值给self的属性,直接访问self.student2本身就是无效的,需要先把创建的实例赋值给对应属性才能在测试方法中调用。
解决方案

方案1(最推荐):改用自增字段类型

如果你的主键就是需要数据库自增生成,直接将IntegerField替换为Django内置的自增字段类型即可,后续create返回的实例会自动携带生成的主键:

class Student(models.Model):
    # 替换为AutoField或者适配大数据量的BigAutoField
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=20)
    score = models.DecimalField(max_digits=10, decimal_places=2)

同时修改测试代码的setUp逻辑,将创建的实例绑定到self:

def setUp(self):
    self.student1 = Student.objects.create(name="student1", score=50)
    self.student2 = Student.objects.create(name="student2", score=55)
    self.student3 = Student.objects.create(name="student3", score=80)
    self.student4 = Student.objects.create(name="student4", score=36)

方案2:坚持使用IntegerField的两种处理方式

如果确实有特殊需求必须使用IntegerField作为主键,可以选择以下任意一种方式获取主键:

  • 方式一:创建实例时主动传入主键值
    self.student2 = Student.objects.create(id=2, name="student2", score=55)
    
    主动传入的主键值会直接保存在实例中,不需要额外操作。
  • 方式二:创建完成后调用refresh_from_db()同步数据库数据
    self.student2 = Student.objects.create(name="student2", score=55)
    # 主动从数据库拉取最新字段值回填到实例
    self.student2.refresh_from_db()
    
    执行完成后就可以通过self.student2.pk获取到数据库生成的主键。

内容的提问来源于stack exchange,提问作者Visrut

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 04:39:01