Z wielu powodów ^ chciałbym użyć UUID jako klucza podstawowego w niektórych moich modelach Django. Jeśli to zrobię, czy nadal będę mógł korzystać z aplikacji zewnętrznych, takich jak „contrib.comments”, „django-voting” lub „django-tagging”, które używają ogólnych relacji za pośrednictwem ContentType?
Na przykładzie „django-voting” model Vote wygląda następująco:
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)
Ta aplikacja wydaje się zakładać, że kluczem podstawowym modelu, na który głosowano, jest liczba całkowita.
Wydaje się, że wbudowana aplikacja do komentarzy jest w stanie obsłużyć niecałkowite 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")
Czy ten problem z zakładaniem liczby całkowitej PK jest powszechną sytuacją w przypadku aplikacji innych firm, która utrudniałaby używanie identyfikatorów UUID? A może źle odczytałem tę sytuację?
Czy istnieje sposób na użycie identyfikatorów UUID jako kluczy podstawowych w Django bez powodowania większych problemów?
^ Niektóre z powodów: ukrywanie liczby obiektów, zapobieganie indeksowaniu adresów URL, używanie wielu serwerów do tworzenia obiektów nie powodujących konfliktów, ...
default
.django_extensions.db.fields.UUIDField
jak wspomniał mitchf, nie będziesz miał problemów z migracjami Django-South - wspomniane przez niego pole ma wbudowaną obsługę migracji południowych.Jak widać w dokumentacji , od Django 1.8 jest wbudowane pole UUID. Różnice w wydajności podczas używania identyfikatora UUID w porównaniu z liczbą całkowitą są pomijalne.
import uuid from django.db import models class MyUUIDModel(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
Możesz również sprawdzić tę odpowiedź, aby uzyskać więcej informacji.
źródło
Natknąłem się na podobną sytuację i dowiedziałem się w oficjalnej dokumentacji Django , że
object_id
nie musi być tego samego typu, co klucz primary_key powiązanego modelu. Na przykład, jeśli chcesz, aby Twój związek generic być ważna zarówno dla IntegerField i Charfield id, zaledwie Określobject_id
być Charfield . Ponieważ liczby całkowite mogą przekształcić się w łańcuchy, będzie dobrze. To samo dotyczy UUIDField .Przykład:
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)
źródło
Prawdziwym problemem związanym z UUID jako PK jest fragmentacja dysku i degradacja wstawki związana z identyfikatorami nieliczbowymi. Ponieważ PK jest indeksem klastrowym, gdy nie jest on automatycznie zwiększany, silnik bazy danych będzie musiał odwołać się do dysku fizycznego podczas wstawiania wiersza z identyfikatorem o niższej liczbie porządkowej, co będzie się działo przez cały czas z identyfikatorami UUID. Gdy masz dużo danych w swojej bazie danych, wstawienie jednego nowego rekordu może zająć wiele sekund lub nawet minut. W końcu dysk zostanie pofragmentowany, co będzie wymagało okresowej defragmentacji dysku. To wszystko jest naprawdę złe.
Aby je rozwiązać, niedawno wymyśliłem następującą architekturę, którą moim zdaniem warto podzielić.
Pseudo-klucz-podstawowy UUID
Ta metoda pozwala wykorzystać zalety UUID jako klucza podstawowego (przy użyciu unikalnego indeksu UUID), przy jednoczesnym zachowaniu automatycznie zwiększanej wartości PK w celu rozwiązania problemu fragmentacji i obniżenia wydajności w przypadku posiadania nieliczbowej PK.
Jak to działa:
pkid
w modelach bazy danych.id
pole UUID, aby umożliwić wyszukiwanie według identyfikatora UUID zamiast numerycznego klucza podstawowego.to_field='id'
), aby umożliwić Twoim kluczom obcym prawidłowe reprezentowanie Pseudo-PK zamiast numerycznego identyfikatora.Zasadniczo wykonasz następujące czynności:
Najpierw utwórz abstrakcyjny model bazowy 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
Pamiętaj, aby rozszerzyć model podstawowy zamiast modeli
class Site(UUIDModel): name = models.CharField(max_length=255)
Upewnij się również, że klucze obce wskazują
id
pole UUID zamiast pola automatycznie zwiększanegopkid
:class Page(UUIDModel): site = models.ForeignKey(Site, to_field='id', on_delete=models.CASCADE)
Jeśli używasz Django Rest Framework (DRF), upewnij się, że utworzyłeś również klasę Base ViewSet, aby ustawić domyślne pole wyszukiwania:
class UUIDModelViewSet(viewsets.ModelViewSet): lookup_field = 'id'
I rozszerz to zamiast podstawowego ModelViewSet dla widoków interfejsu API:
class SiteViewSet(UUIDModelViewSet): model = Site class PageViewSet(UUIDModelViewSet): model = Page
Więcej informacji o tym, dlaczego i jak, w tym artykule: https://www.stevenmoseley.com/blog/uuid-primary-keys-django-rest-framework-2-steps
źródło
można to zrobić przy użyciu niestandardowego podstawowego modelu abstrakcyjnego, wykonując następujące kroki.
Najpierw utwórz folder w swoim projekcie, nazwij go basemodel, a następnie dodaj plik abstractmodelbase.py z poniższym:
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']
po drugie: we wszystkich plikach modelu dla każdej aplikacji zrób to
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)
Zatem powyższy incydent związany z modelem jest nieodłącznym elementem całego pola w modelu bazowym.
źródło
Pytanie można przeformułować w następujący sposób: „czy istnieje sposób, aby Django używał UUID dla wszystkich identyfikatorów bazy danych we wszystkich tabelach zamiast automatycznie zwiększanej liczby całkowitej?”.
Jasne, mogę zrobić:
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
we wszystkich moich tabelach, ale nie mogę znaleźć sposobu, aby to zrobić dla:
Wydaje się więc, że jest to brakująca funkcja Django.
źródło