По ряду причин ^ я хотел бы использовать UUID в качестве первичного ключа в некоторых моих моделях Django. Если я это сделаю, смогу ли я использовать внешние приложения, такие как contrib.comments, django-vote или django-tagging, которые используют общие отношения через ContentType?
На примере «django-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 может стать проблемой? Или, возможно, я неправильно понимаю эту ситуацию?
Есть ли способ использовать UUID в качестве первичных ключей в Django, не вызывая особых проблем?
^ Некоторые из причин: сокрытие количества объектов, предотвращение обхода URL-адресов, использование нескольких серверов для создания неконфликтующих объектов, ...
default
.django_extensions.db.fields.UUIDField
как указано mitchf, у вас не будет проблем с миграциями Django-South - поле, указанное им, имеет встроенную поддержку миграции South.Как видно из документации , в 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)
Вы также можете проверить этот ответ для получения дополнительной информации.
источник
Я столкнулся с аналогичной ситуацией и обнаружил в официальной документации Django , что
object_id
не обязательно должен быть того же типа, что и primary_key соответствующей модели. Например, если вы хотите, чтобы ваше общее отношение было действительным для идентификаторов IntegerField и CharField , просто установите для вас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 в качестве PK - это фрагментация диска и ухудшение качества вставки, связанное с нечисловыми идентификаторами. Поскольку PK является кластеризованным индексом, когда он не увеличивается автоматически, вашему движку БД придется прибегать к вашему физическому диску при вставке строки с идентификатором более низкого порядка, что будет происходить все время с UUID. Когда вы получаете много данных в своей БД, вставка одной новой записи может занять много секунд или даже минут. И ваш диск со временем станет фрагментированным, что потребует периодической дефрагментации диска. Это все очень плохо.
Чтобы решить эту проблему, я недавно придумал следующую архитектуру, которой, как мне казалось, стоит поделиться.
Псевдо-первичный ключ UUID
Этот метод позволяет вам использовать преимущества UUID в качестве первичного ключа (с использованием уникального индекса UUID), сохраняя при этом автоматически увеличивающийся PK для устранения фрагментации и вставки проблем снижения производительности из-за наличия нечислового PK.
Как это работает:
pkid
в ваших моделях БД.id
поле UUID с уникальным индексом, чтобы можно было выполнять поиск по идентификатору UUID вместо числового первичного ключа.to_field='id'
), чтобы ваши внешние ключи правильно представляли псевдо-PK вместо числового идентификатора.По сути, вы сделаете следующее:
Сначала создайте абстрактную базовую модель Django
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
Обязательно расширяйте базовую модель, а не модели.
class Site(UUIDModel): name = models.CharField(max_length=255)
Также убедитесь, что ваши ForeignKeys указывают на
id
поле UUID вместо автоматически увеличивающегося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'
И расширите это вместо базового ModelViewSet для ваших представлений API:
class SiteViewSet(UUIDModelViewSet): model = Site class PageViewSet(UUIDModelViewSet): model = Page
Дополнительные примечания о том, почему и как, в этой статье: https://www.stevenmoseley.com/blog/uuid-primary-keys-django-rest-framework-2-steps
источник
это можно сделать с помощью пользовательской базовой абстрактной модели, выполнив следующие шаги.
Сначала создайте папку в своем проекте, назовите ее 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']
второй: во всем файле модели для каждого приложения сделайте это
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)
Таким образом, описанная выше модель присуща всем областям базовой абстрактной модели.
источник
Вопрос можно перефразировать как «есть ли способ заставить Django использовать UUID для всех идентификаторов баз данных во всех таблицах вместо автоматически увеличивающегося целого числа?».
Конечно, я могу:
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
во всех моих таблицах, но я не могу найти способ сделать это для:
Итак, похоже, что это отсутствующая функция Django.
источник