出于多种原因^,我想在某些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作为主键而不会造成太多麻烦?
[UUID主键不仅会导致通用关系问题,而且会给整体效率带来问题:每个外键都比机器单词昂贵得多,无论是存储还是加入都非常昂贵。
但是,没有什么要求将UUID用作主键:只需将其添加为unique=True
的uuid字段,即可使其成为secondary键。照常使用隐式主键(在系统内部),并使用UUID作为外部标识符。
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以获取更多信息。
我遇到了类似的情况,并在official Django documentation中发现,object_id
不必与相关模型的primary_key具有相同的类型。例如,如果您希望您的通用关系对IntegerField和CharField 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)
我最近提出了以下架构来解决我认为值得分享的问题。
UUID伪主键
[此方法可让您利用UUID作为伪PK(使用唯一索引)的好处,同时保持自动递增的PK来解决具有非数字PK的性能问题(碎片,插入降解,等)
工作原理:
pkid
的自动递增主键。id
字段,以使您可以按UUID ID(而不是数字主键)进行搜索。 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
这个问题可以改写为:“是否有一种方法可以让Django在所有表中为所有数据库ID使用UUID而不是自动递增的整数?”]]
当然,我可以这样做:
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
在我所有的表格中,但我找不到以下方法:
因此,这似乎是缺少的Django功能。