Pour un certain nombre de raisons ^, j'aimerais utiliser un UUID comme clé primaire dans certains de mes modèles Django. Si je le fais, est-ce que je pourrai toujours utiliser des applications extérieures comme "contrib.comments", "django -oting" ou "django-tagging" qui utilisent des relations génériques via ContentType?
En utilisant "django-vote" comme exemple, le modèle Vote ressemble à ceci:
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)
Cette application semble supposer que la clé primaire du modèle soumis au vote est un entier.
L'application de commentaires intégrée semble être capable de gérer les PK non entiers, cependant:
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")
Ce problème «supposé PK entier» est-il une situation courante pour les applications tierces qui rendraient l'utilisation des UUID pénibles? Ou, peut-être, ai-je mal interprété cette situation?
Existe-t-il un moyen d'utiliser les UUID comme clés primaires dans Django sans causer trop de problèmes?
^ Certaines des raisons: masquer le nombre d'objets, empêcher l'exploration d'url "id crawling", utiliser plusieurs serveurs pour créer des objets non conflictuels, ...
default
.django_extensions.db.fields.UUIDField
comme mentionné par mitchf, vous n'aurez aucun problème avec les migrations Django-Sud - le champ mentionné par lui a un support intégré pour les migrations Sud.Comme vu dans la documentation , à partir de Django 1.8, il existe un champ UUID intégré. Les différences de performances lors de l'utilisation d'un UUID par rapport à un entier sont négligeables.
import uuid from django.db import models class MyUUIDModel(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
Vous pouvez également consulter cette réponse pour plus d'informations.
la source
Je suis tombé sur une situation similaire et j'ai découvert dans la documentation officielle de Django , que le
object_id
ne doit pas nécessairement être du même type que le primary_key du modèle associé. Par exemple, si vous souhaitez que votre relation générique soit valide pour les identifiants IntegerField et CharField , définissez simplement votreobject_id
comme CharField . Étant donné que les entiers peuvent être convertis en chaînes, tout ira bien. Il en va de même pour UUIDField .Exemple:
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)
la source
Le vrai problème avec l'UUID en tant que PK est la fragmentation du disque et la dégradation des insertions associées aux identifiants non numériques. Étant donné que le PK est un index clusterisé, lorsqu'il n'est pas auto-incrémenté, votre moteur de base de données devra recourir à votre lecteur physique lors de l'insertion d'une ligne avec un identifiant d'ordinal inférieur, ce qui se produira tout le temps avec les UUID. Lorsque vous obtenez beaucoup de données dans votre base de données, cela peut prendre plusieurs secondes, voire quelques minutes, simplement pour insérer un nouvel enregistrement. Et votre disque finira par devenir fragmenté, ce qui nécessitera une défragmentation périodique du disque. Tout cela est vraiment mauvais.
Pour résoudre ces problèmes, j'ai récemment proposé l'architecture suivante qui, à mon avis, mériterait d'être partagée.
La pseudo-clé primaire UUID
Cette méthode vous permet de tirer parti des avantages d'un UUID en tant que clé primaire (à l'aide d'un UUID d'index unique), tout en conservant une PK auto-incrémentée pour résoudre la fragmentation et insérer les problèmes de dégradation des performances liés à une PK non numérique.
Comment ça fonctionne:
pkid
sur vos modèles de base de données.id
champ UUID indexé unique pour vous permettre de rechercher par un identifiant UUID, au lieu d'une clé primaire numérique.to_field='id'
) pour permettre à vos clés étrangères de représenter correctement le Pseudo-PK au lieu de l'ID numérique.Essentiellement, vous effectuerez les opérations suivantes:
Tout d'abord, créez un modèle de base Django abstrait
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
Assurez-vous d'étendre le modèle de base au lieu des modèles.
class Site(UUIDModel): name = models.CharField(max_length=255)
Assurez-vous également que vos ForeignKeys pointent vers l'UUID
id
champ au lieu du champ auto-incrémentépkid
:class Page(UUIDModel): site = models.ForeignKey(Site, to_field='id', on_delete=models.CASCADE)
Si vous utilisez Django Rest Framework (DRF), assurez-vous de créer également une classe Base ViewSet pour définir le champ de recherche par défaut:
class UUIDModelViewSet(viewsets.ModelViewSet): lookup_field = 'id'
Et étendez cela au lieu du ModelViewSet de base pour vos vues API:
class SiteViewSet(UUIDModelViewSet): model = Site class PageViewSet(UUIDModelViewSet): model = Page
Plus de notes sur le pourquoi et le comment dans cet article: https://www.stevenmoseley.com/blog/uuid-primary-keys-django-rest-framework-2-steps
la source
cela peut être fait en utilisant un modèle abstrait de base personnalisé, en utilisant les étapes suivantes.
Créez d'abord un dossier dans votre projet, appelez-le basemodel, puis ajoutez un abstractmodelbase.py avec ce qui suit ci-dessous:
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']
deuxième: dans tout votre fichier de modèle pour chaque application, faites ceci
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)
Ainsi, l'incident du modèle ci-dessus est inhérent à tout le champ du modèle abstrait de base.
la source
La question peut être reformulée comme suit: "existe-t-il un moyen pour que Django utilise un UUID pour tous les identifiants de base de données dans toutes les tables au lieu d'un entier auto-incrémenté?".
Bien sûr, je peux faire:
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
dans toutes mes tables, mais je ne trouve pas de moyen de le faire pour:
Donc, cela semble être une fonctionnalité Django manquante.
la source