2010-10-14 7 views
54

Per una serie di ragioni, vorrei utilizzare un UUID come chiave primaria in alcuni dei miei modelli Django. Se lo faccio, potrò comunque utilizzare app esterne come "contrib.comments", "django-voting" o "django-tagging" che usano relazioni generiche tramite ContentType?Utilizzo di un UUID come chiave primaria nei modelli Django (impatto delle relazioni generiche)

Utilizzando "django-voto" come esempio, il modello di voto si presenta così:

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) 

Questa applicazione sembra essere ipotizzando che la chiave primaria per il modello di essere votato è un numero intero.

La commenti app built-in sembra essere in grado di gestire PKs non interi, però:

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") 

È questo il problema "integer-PK-assunto" una situazione comune per le applicazioni di terze parti, che avrebbe rendere l'uso di UUID un dolore? O, forse, sto fraintendendo questa situazione?

C'è un modo per utilizzare gli UUID come chiavi primarie in Django senza causare troppi problemi?


^ Alcuni dei motivi: la conta degli oggetti nascosti, impedendo url "id crawling", l'utilizzo di più server per creare oggetti non in conflitto, ...

risposta

39

Una chiave primaria UUID causerà problemi non solo con relazioni generiche, ma con efficienza in generale: ogni chiave esterna sarà significativamente più costosa, sia per memorizzare, sia per partecipare su una parola macchina.

Tuttavia, nulla richiede l'UUID di essere la chiave primaria: basta fare un secondario chiave , integrando il modello con un campo uuid con unique=True. Utilizzare normalmente la chiave primaria implicita (interna al sistema) e utilizzare l'UUID come identificativo esterno.

+0

Inoltre, è possibile ignorare 'save' e generare il vostro UUID lì quando un oggetto viene salvato per la prima volta (verificando se l'oggetto ha una chiave primaria). –

+13

Joe Holloway, non c'è bisogno di questo: si può semplicemente fornire la funzione di generazione UUID come 'predefinita 'del campo. –

+1

Grazie Piet. La tua soluzione è ciò che sto facendo ora e funziona per oscurare la chiave primaria nell'URI (sebbene l'app per i commenti lo mostri ancora in un campo nascosto nel modulo "crea commento").Non mi dà il vantaggio di poter creare facilmente file di database non in collisione su server separati. Oh beh, credo che imparerò a ri-amare la chiave primaria intera. – mitchf

135

Django 1.8 ora ha un campo UUID integrato. Le differenze di prestazioni quando si utilizza un UUID vs intero sono trascurabili.

import uuid 
from django.db import models 

class MyUUIDModel(models.Model): 
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) 
+5

Questo risolve i problemi di relazioni generiche? –

+0

@Keithhackbarth come si imposta django per usarlo ogni volta quando si creano automaticamente gli ID per le tabelle? – anon58192932

+2

@ anon58192932 Non è chiaro cosa intendi esattamente "ogni volta". Se si desidera utilizzare gli UUID per ogni modello, creare il proprio modello di base astratto e utilizzarlo al posto di django.models.Model. –

8

mi sono imbattuto in una situazione simile e scoperto nel official Django documentation, che la object_id non deve essere dello stesso tipo della primary_key del modello relativo. Ad esempio, se si desidera che il vostro rapporto generico per essere valido sia per IntegerField e CharField id, basta impostare il object_id essere un CharField. Poiché gli interi possono forzare le stringhe, andrà bene. Lo stesso vale per UUIDField.

Esempio:

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)