在Django模型中使用UUID作为主键(对通用关系有影响)

问题描述 投票:78回答:5

出于多种原因^,我想在某些Django模型中使用UUID作为主键。如果这样做,我仍然可以使用通过ContentType使用通用关系的外部应用程序,例如“ contrib.comments”,“ django-voting”或“ django-tagging”吗?

以“ django-voting”为例,投票模型如下:

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爬行”,使用多个服务器创建不冲突的对象,...
django django-models primary-key content-type uuid
5个回答
45
投票

[UUID主键不仅会导致通用关系问题,而且会给整体效率带来问题:每个外键都比机器单词昂贵得多,无论是存储还是加入都非常昂贵。

但是,没有什么要求将UUID用作主键:只需将其添加为unique=True的uuid字段,即可使其成为secondary键。照常使用隐式主键(在系统内部),并使用UUID作为外部标识符。


188
投票

As seen in the documentation,从Django 1.8开始,有一个内置的UUID字段。使用UUID与整数时的性能差异可以忽略不计。

import uuid
from django.db import models

class MyUUIDModel(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)

您也可以check this answer以获取更多信息。


10
投票

我遇到了类似的情况,并在official Django documentation中发现,object_id不必与相关模型的primary_key具有相同的类型。例如,如果您希望您的通用关系对IntegerFieldCharField id均有效,则只需将object_id设置为CharField。由于整数可以强制转换为字符串,所以会很好。 UUIDField也是如此。

示例:

class Vote(models.Model):
    user         = models.ForeignKey(User)
    content_type = models.ForeignKey(ContentType)
    object_id    = models.CharField(max_length=50) # <<-- This line was modified 
    object       = generic.GenericForeignKey('content_type', 'object_id')
    vote         = models.SmallIntegerField(choices=SCORES)

0
投票

我最近提出了以下架构来解决我认为值得分享的问题。

UUID伪主键

[此方法可让您利用UUID作为伪PK(使用唯一索引)的好处,同时保持自动递增的PK来解决具有非数字PK的性能问题(碎片,插入降解,等)

工作原理:

  1. 在数据库模型上创建一个名为pkid的自动递增主键。
  2. 添加唯一索引的UUID id字段,以使您可以按UUID ID(而不是数字主键)进行搜索。
  3. 将ForeighKey指向UUID(使用to_field='id'),以允许您的外键正确地表示伪PK,而不是数字ID。

基本上,您将执行以下操作:

首先,创建Django基本模型

class UUIDModel(models.Model):
    pkid = models.BigAutoField(primary_key=True, editable=False)
    id = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)

请确保扩展基本模型而不是模型。

class Site(UUIDModel):
    name = models.CharField(max_length=255)

还请确保您的外键指向UUID id字段,而不是自动递增的pkid字段:

class Page(UUIDModel):
    site = models.ForeignKey(Site, to_field='id', on_delete=models.CASCADE)

如果使用的是Django Rest Framework(DRF),请确保还创建一个Base ViewSet类来设置默认搜索字段:

class UUIDModelViewSet(viewsets.ModelViewSet):
    lookup_field = 'id' 

并为您的API视图扩展它而不是基本ModelViewSet:

class SiteViewSet(BaseModelViewSet):
    model = Site

class PageViewSet(BaseModelViewSet):
    model = Page

本文中关于为什么和如何的更多注释:https://www.stevenmoseley.com/blog/uuid-primary-keys-django-rest-framework-2-steps


-1
投票

这个问题可以改写为:“是否有一种方法可以让Django在所有表中为所有数据库ID使用UUID而不是自动递增的整数?”]]

当然,我可以这样做:

id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)

在我所有的表格中,但我找不到以下方法:

  1. 第三方模块
  2. Django生成的ManyToMany表
  3. 因此,这似乎是缺少的Django功能。

最新问题
© www.soinside.com 2019 - 2024. All rights reserved.