在Django项目里,默认的主键是自增的整数ID,这种ID有规律可循,很容易被攻击者通过遍历ID的方式批量获取系统内的数据,存在数据泄露的风险。使用UUID作为主键可以有效避免这个问题,因为UUID没有规律,几乎不可能被遍历猜测。

UUIDField基础介绍
Django内置了UUIDField字段类型,专门用来存储UUID值,它在数据库层面会对应合适的UUID存储类型,比如PostgreSQL的uuid类型,SQLite的char(32)类型等。UUID是通用唯一识别码,由32个十六进制字符组成,通过特定的算法生成,重复概率极低。
配置UUID作为主键的步骤
1. 定义模型时使用UUIDField作为主键
在模型的属性定义中,声明id字段为UUIDField,设置primary_key=True,同时指定默认值为生成UUID的函数,避免使用自增ID。以下是基础的用户模型示例:
import uuid
from django.db import models
class User(models.Model):
# 设置UUID为主键,默认值为uuid.uuid4生成的新UUID
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
username = models.CharField(max_length=30, unique=True)
create_time = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.username
这里的editable=False表示该字段在后台管理界面等场景中不可编辑,default=uuid.uuid4会在创建新实例时自动生成唯一的UUID值,不需要手动赋值。
2. 执行数据库迁移
定义好模型之后,需要执行Django的迁移命令让配置生效:
python manage.py makemigrations python manage.py migrate
如果是已经存在的项目,原本使用的是自增ID主键,需要先处理原有数据,再修改主键配置,否则迁移过程会出现冲突,建议先备份数据再操作。
3. 关联表的外键配置
如果其他模型需要关联这个UUID主键的模型,外键字段会自动适配UUID类型,不需要额外特殊配置,示例如下:
import uuid
from django.db import models
class User(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
username = models.CharField(max_length=30, unique=True)
class Order(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
# 外键关联User模型,会自动使用UUID类型
user = models.ForeignKey(User, on_delete=models.CASCADE)
order_no = models.CharField(max_length=50)
UUID主键的优缺点分析
优点
- 防止ID遍历:UUID没有规律,攻击者无法通过递增的方式批量获取数据,提升数据安全性。
- 分布式场景友好:不同服务生成的UUID不会冲突,适合分布式系统下的数据同步场景。
- 不需要提前获取ID:生成UUID不需要依赖数据库的自增序列,减少数据库交互。
缺点
- 存储空间更大:UUID字符串长度比整数长,会占用更多的数据库存储空间,尤其是数据量大的时候差异更明显。
- 查询性能略低:UUID作为主键的索引效率比整数自增ID稍低,对查询性能有一定影响,不过在大部分中小型项目中差异可以忽略。
- 可读性差:UUID是一串无意义的字符,调试的时候不如整数ID直观。
使用注意事项
首先,不要在业务逻辑中手动修改UUID主键的值,因为默认配置了editable=False,手动修改可能导致主键冲突。其次,如果项目数据量非常大,对查询性能要求极高,需要谨慎评估是否使用UUID主键,或者可以采用整数ID作为内部主键,对外暴露UUID作为业务标识的方案。最后,生成UUID的时候要使用uuid.uuid4()方法,不要使用基于时间戳或者MAC地址生成的UUID版本,避免泄露服务器相关信息。