mit*_*chf 81 django uuid content-type primary-key django-models
出于多种原因^,我想在我的一些Django模型中使用UUID作为主键.如果我这样做,我是否仍然可以使用"contrib.com","django-voting"或"django-tagging"之类的外部应用程序,它们通过ContentType使用通用关系?
以"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)
Run Code Online (Sandbox Code Playgroud)
这个应用程序似乎假设被投票的模型的主键是一个整数.
内置的评论应用程序似乎能够处理非整数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")
Run Code Online (Sandbox Code Playgroud)
对于第三方应用程序而言,这种"整数假定PK"问题是否会成为一种常见情况,这会使使用UUID变得痛苦?或者,我可能会误读这种情况吗?
有没有办法在Django中使用UUID作为主键而不会造成太多麻烦?
kei*_*rth 191
从文档中可以看出,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)
Run Code Online (Sandbox Code Playgroud)
您还可以查看此答案以获取更多信息.
Pi *_*ort 46
UUID主键不仅会导致通用关系出现问题,而且通常会带来效率问题:每个外键的存储和加入都比机器字要贵得多.
但是,没有任何东西需要UUID作为主键:只需将其作为辅助键,通过使用uuid字段补充模型unique=True
.正常使用隐式主键(系统内部),并使用UUID作为外部标识符.
Jor*_*rdi 11
我遇到了类似的情况,并在官方Django文档中发现,它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)
Run Code Online (Sandbox Code Playgroud)
Ste*_*ley 11
UUID 作为 PK 的真正问题是与非数字标识符相关的磁盘碎片和插入降级。因为 PK 是一个聚集索引(几乎在除 PostgreSQL 之外的每个 RDBMS 中),当它不是自动递增时,您的数据库引擎在插入具有较低序数的行时将不得不求助于您的物理驱动器,这将一直发生带有 UUID。当您在数据库中获得大量数据时,插入一条新记录可能需要数秒甚至数分钟。并且您的磁盘最终会变得碎片化,需要定期进行磁盘碎片整理。这一切都非常糟糕。
为了解决这些问题,我最近提出了以下我认为值得分享的架构。
UUID 伪主键
此方法允许您利用 UUID 作为主键的优势(使用唯一索引 UUID),同时维护自动递增的 PK 以解决非数字 PK 的碎片和插入性能下降问题。
这个怎么运作:
pkid
在您的数据库模型上调用的自动递增主键。id
字段以允许您按 UUID id 而非数字主键进行搜索。to_field='id'
)以允许您的外键正确表示 Pseudo-PK 而不是数字 ID。基本上,您将执行以下操作:
首先,创建一个抽象的Django Base Model
class UUIDModel(models.Model):
pkid = models.BigAutoField(primary_key=True, editable=False)
id = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
class Meta:
abstract = True
Run Code Online (Sandbox Code Playgroud)
确保扩展基本模型而不是 models.Model
class Site(UUIDModel):
name = models.CharField(max_length=255)
Run Code Online (Sandbox Code Playgroud)
还要确保您的 ForeignKeys 指向 UUIDid
字段而不是自动增加的pkid
字段:
class Page(UUIDModel):
site = models.ForeignKey(Site, to_field='id', on_delete=models.CASCADE)
Run Code Online (Sandbox Code Playgroud)
如果您使用的是 Django Rest Framework (DRF),请确保还创建一个 Base ViewSet 类来设置默认搜索字段:
class UUIDModelViewSet(viewsets.ModelViewSet):
lookup_field = 'id'
Run Code Online (Sandbox Code Playgroud)
并为您的 API 视图扩展它而不是基础 ModelViewSet:
class SiteViewSet(UUIDModelViewSet):
model = Site
class PageViewSet(UUIDModelViewSet):
model = Page
Run Code Online (Sandbox Code Playgroud)
关于本文中的原因和方法的更多说明:https : //www.stevenmoseley.com/blog/uuid-primary-keys-django-rest-framework-2-steps
小智 7
这可以通过使用自定义基本抽象模型来完成,步骤如下。
首先在项目中创建一个文件夹,将其命名为basemodel,然后添加一个abstractmodelbase.py,其中包含以下内容:
from django.db import models
import uuid
class BaseAbstractModel(models.Model):
"""
This model defines base models that implements common fields like:
created_at
updated_at
is_deleted
"""
id = models.UUIDField(primary_key=True, unique=True, default=uuid.uuid4, editable=False)
created_at = models.DateTimeField(auto_now_add=True, editable=False)
updated_at = models.DateTimeField(auto_now=True, editable=False)
is_deleted = models.BooleanField(default=False)
def soft_delete(self):
"""soft delete a model instance"""
self.is_deleted=True
self.save()
class Meta:
abstract = True
ordering = ['-created_at']
Run Code Online (Sandbox Code Playgroud)
第二:在每个应用程序的所有模型文件中执行此操作
from django.db import models
from basemodel import BaseAbstractModel
import uuid
# Create your models here.
class Incident(BaseAbstractModel):
""" Incident model """
place = models.CharField(max_length=50, blank=False, null=False)
personal_number = models.CharField(max_length=12, blank=False, null=False)
description = models.TextField(max_length=500, blank=False, null=False)
action = models.TextField(max_length=500, blank=True, null=True)
image = models.ImageField(upload_to='images/', blank=True, null=True)
incident_date = models.DateTimeField(blank=False, null=False)
Run Code Online (Sandbox Code Playgroud)
因此,上述模型事件固有了基础抽象模型中的所有字段。