views:

94

answers:

1

For a number of reasons^, I'd like to use a UUID as a primary key in some of my Django models. If I do so, will I still be able to use outside apps like "contrib.comments", "django-voting" or "django-tagging" which use generic relations via ContentType?

Using "django-voting" as an example, the Vote model looks like this:

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)

This app seems to be assuming that the primary key for the model being voted on is an integer.

The built-in comments app seems to be capable of handling non-integer PKs, though:

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

Is this "integer-PK-assumed" problem a common situation for third-party apps which would make using UUIDs a pain? Or, possibly, am I misreading this situation?

Is there a way to use UUIDs as primary keys in Django without causing too much trouble?


^ Some of the reasons: hiding object counts, preventing url "id crawling", using multiple servers to create non-conflicting objects, ...

+3  A: 

A UUID primary key will cause problems not only with generic relations, but with efficiency in general: every foreign key will be significantly more expensive—both to store, and to join on—than a machine word.

However, nothing requires the UUID to be the primary key: just make it a secondary key, by supplementing your model with a uuid field with unique=True. Use the implicit primary key as normal (internal to your system), and use the UUID as your external identifier.

Piet Delport
In addition, you can override `save` and generate your UUID there when an object is being saved the first time (by checking if the object has a primary key).
Joe Holloway
Joe Holloway, no need for that: you can simply supply the UUID generation function as the field's `default`.
Piet Delport
Thanks Piet. Your solution is what I'm doing now and it works for obscuring the primary key in the URI (although the comment app still shows it in a hidden field in the "create comment" form). Doesn't give me the advantage of being able to easily create non-colliding database rows on separate servers though. Oh well, I guess I'll learn to re-love the integer primary key.
mitchf
Joe: I use django_extensions.db.fields.UUIDField to create my UUIDs in my model. It's simple, I just define my field like this:user_uuid = UUIDField()
mitchf