Django ORM使用Postgres RandomUUID作为主键save后无法获取主键值
问题根因
你将PostgreSQL内置的RandomUUID函数直接作为UUIDField的默认值,该UUID是数据库执行INSERT语句时才生成的,Django ORM默认不会主动拉取数据库生成的主键值回写到实例中,因此你的实例id字段存储的是RandomUUID()函数表达式而非实际生成的UUID值。后续查询、刷新操作时,表达式会被再次解析为生成新UUID的SQL,自然无法匹配到已插入的记录。
解决方案
方案1:改用Python层面生成UUID(最推荐)
直接使用Python标准库的uuid.uuid4作为默认值,UUID在实例创建时就会生成,save前后都可以直接读取,兼容所有数据库,没有回写问题。
代码修改示例:
import uuid from django.db import models from django.db.models import JSONField class NewsItem(models.Model): # 仅修改id字段定义即可 id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) # 其余字段保持不变 title = models.ForeignKey(RichText, models.DO_NOTHING, related_name='+', null=True) subtitle = models.ForeignKey(RichText, models.DO_NOTHING, related_name='+', null=True) excerpt = models.ForeignKey(RichText, models.DO_NOTHING, related_name='+', null=True) text = models.ForeignKey(RichText, models.DO_NOTHING, related_name='+', null=True) image = models.ForeignKey(ImageObject, models.DO_NOTHING, related_name='+', null=True) priority = models.IntegerField(null=True, default=0) frequency = models.IntegerField(null=True, default=1) text_effects = JSONField(blank=True, null=True, default=dict) scroll_speed = models.IntegerField(null=True, default=1) scroll_direction = models.IntegerField(null=True, default=0) created_at = models.DateTimeField(blank=True, null=True, auto_now_add=True) updated_at = models.DateTimeField(blank=True, null=True, auto_now=True) class Meta: managed = True db_table = 'news_items'
修改后直接操作即可:
obj = NewsItem(**params) obj.save() print(obj.id) # 直接输出实际生成的UUID值
方案2:保留数据库生成UUID,开启字段回写配置
如果你有业务要求必须由数据库生成UUID(比如其他系统直接操作数据库依赖该默认值),可以给UUIDField添加db_returning=True参数,Django执行INSERT时会自动带上RETURNING id子句,将数据库生成的UUID直接回写到实例中。
仅修改id字段定义即可:
id = models.UUIDField(primary_key=True, default=RandomUUID, editable=False, db_returning=True)
该配置仅支持PostgreSQL、Oracle、SQLite 3.35+等支持RETURNING语句的数据库,你当前使用PostgreSQL可以正常生效。
方案3:旧版本Django兼容方案
如果你的Django版本低于3.2不支持db_returning参数,可以在插入时添加唯一的业务标识字段,插入后通过该字段查询得到实际的UUID值。比如新增一个request_id字段存储临时唯一标识,插入后通过request_id过滤得到刚插入的记录即可。
内容的提问来源于stack exchange,提问作者Haider Ali
相关产品推荐
相关产品推荐

