出于多种原因^,我希望在我的一些Django模型中使用UUID作为主键。如果我这样做了,我还能使用外部应用程序吗,比如“contrib.comments”,“django-voting”或“django-tagging”,这些应用程序通过ContentType使用泛型关系?
以“django-voting”为例,Vote模型如下所示:
class Vote(models.Model):
user = models.ForeignKey(User)
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
object = generic.GenericForeignKey('content_type', 'object_id')
vote = models.SmallIntegerField(choices=SCORES)
这个应用程序似乎假设被投票的模型的主键是一个整数。
内置的评论应用程序似乎能够处理非整数PK,但是:
class BaseCommentAbstractModel(models.Model):
content_type = models.ForeignKey(ContentType,
verbose_name=_('content type'),
related_name="content_type_set_for_%(class)s")
object_pk = models.TextField(_('object ID'))
content_object = generic.GenericForeignKey(ct_field="content_type", fk_field="object_pk")
这种“整数PK假设”的问题是第三方应用程序的常见问题吗?这会让使用UUID变得很痛苦?或者,可能是我误解了这种情况?
有没有一种方法可以在Django中使用UUID作为主键而不引起太多麻烦?
^部分原因:隐藏对象计数、防止URL“ID爬行”、使用多个服务器来创建不冲突的对象...
6条答案
按热度按时间iszxjhcz1#
如文档所示,Django1.8中有一个内置的UUID字段,使用UUID和整数的性能差异可以忽略不计。
您也可以通过check this answer获取更多信息。
m2xkgtsf2#
UUID主键不仅会导致泛型关系的问题,而且通常会导致效率的问题:每个外键的存储和连接都比机器字昂贵得多。
但是,没有什么要求UUID是主键:只需将其作为一个 secondary 键,方法是在模型中添加一个带有
unique=True
的uuid字段,使用隐式主键(系统内部),并使用UUID作为外部标识符。4nkexdtk3#
UUID作为PK的真实的问题是与非数字标识符相关的磁盘碎片和插入降级。(在除PostgreSQL之外的几乎所有RDBMS中),当它不是自动递增时,您的DB引擎将不得不在插入具有较低序号的ID的行时重新选择您的物理驱动器,这种情况在使用UUID时经常发生。当你的数据库中有很多数据时,插入一条新记录可能要花很多秒甚至几分钟。而且你的磁盘最终会变成碎片,需要定期的磁盘碎片整理。这真的很糟糕。
为了解决这些问题,我最近提出了以下架构,我认为值得与大家分享。
UUID伪主键
此方法允许您利用UUID作为主键(使用唯一索引UUID)的优势,同时维护自动递增的PK以解决使用非数字PK时的碎片和插入性能下降问题。
工作原理:
1.在DB模型上创建一个名为
pkid
的自动递增主键。1.添加一个唯一索引的UUID
id
字段,以允许您按UUID id而不是数字主键进行搜索。1.将外键指向UUID(使用
to_field='id'
),以允许外键正确表示伪PK而不是数字ID。基本上,您将执行以下操作:
首先,创建一个抽象的Django基础模型
确保扩展基础模型而不是模型。模型
还要确保外键指向UUID
id
字段,而不是自动递增的pkid
字段:如果您使用Django Rest Framework(DRF),请确保同时创建一个Base ViewSet类来设置默认搜索字段:
并扩展它,而不是API视图的基础ModelViewSet:
关于本文中原因和方法的更多说明:https://www.stevenmoseley.com/blog/uuid-primary-keys-django-rest-framework-2-steps
pobjuy324#
我遇到过类似的情况,在Django的官方文档中发现,
object_id
不必与相关模型的primary_key为同一类型。例如,如果您希望泛型关系对IntegerField和CharFieldid都有效,只要把你的object_id
设置成一个CharField。2因为整数可以强制转换成字符串,所以就可以了。3同样的方法也适用于UIDField。kcrjzv8t5#
这可以通过使用定制基础抽象模型、使用以下步骤来完成。
首先在您的项目中创建一个文件夹,将其命名为basemodel,然后添加一个abstractmodelbase.py,内容如下:
第二步:在所有应用程序模型文件中执行此操作
因此上述模型与基本抽象模型中的所有领域相关联。
o4tp2gmn6#
这个问题可以改写为"有没有办法让Django对所有表中的所有数据库ID使用UUID,而不是自动递增的整数?"
当然,我能做到:
在我所有的表中,但我找不到一种方法来执行此操作:
1.第三方模块
所以,这似乎是Django缺少的一个特性。