Created
October 30, 2010 13:52
-
-
Save mathieusteele/655314 to your computer and use it in GitHub Desktop.
UUIDField type for django projects
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import uuid | |
from django.forms.util import ValidationError | |
from django import forms | |
from django.db import models | |
from django.utils.encoding import smart_unicode | |
from django.utils.translation import ugettext_lazy | |
class UUIDVersionError(Exception): | |
pass | |
class UUIDField(models.CharField): | |
"""Encode and stores a Python uuid.UUID in a manner that is appropriate | |
for the given datatabase that we are using. | |
For sqlite3 or MySQL we save it as a 36-character string value | |
For PostgreSQL we save it as a uuid field | |
This class supports type 1, 2, 4, and 5 UUID's. | |
""" | |
__metaclass__ = models.SubfieldBase | |
_CREATE_COLUMN_TYPES = { | |
'postgresql_psycopg2': 'uuid', | |
'postgresql': 'uuid' | |
} | |
def __init__(self, verbose_name=None, name=None, auto=True, version=1, node=None, clock_seq=None, namespace=None, **kwargs): | |
"""Contruct a UUIDField. | |
@param verbose_name: Optional verbose name to use in place of what | |
Django would assign. | |
@param name: Override Django's name assignment | |
@param auto: If True, create a UUID value if one is not specified. | |
@param version: By default we create a version 1 UUID. | |
@param node: Used for version 1 UUID's. If not supplied, then the uuid.getnode() function is called to obtain it. This can be slow. | |
@param clock_seq: Used for version 1 UUID's. If not supplied a random 14-bit sequence number is chosen | |
@param namespace: Required for version 3 and version 5 UUID's. | |
@param name: Required for version4 and version 5 UUID's. | |
See Also: | |
- Python Library Reference, section 18.16 for more information. | |
- RFC 4122, "A Universally Unique IDentifier (UUID) URN Namespace" | |
If you want to use one of these as a primary key for a Django | |
model, do this:: | |
id = UUIDField(primary_key=True) | |
This will currently I{not} work with Jython because PostgreSQL support | |
in Jython is not working for uuid column types. | |
""" | |
self.max_length = 36 | |
kwargs['max_length'] = self.max_length | |
if auto: | |
kwargs['blank'] = True | |
kwargs.setdefault('editable', False) | |
self.auto = auto | |
self.version = version | |
if version==1: | |
self.node, self.clock_seq = node, clock_seq | |
elif version==3 or version==5: | |
self.namespace, self.name = namespace, name | |
super(UUIDField, self).__init__(verbose_name=verbose_name, | |
name=name, **kwargs) | |
def create_uuid(self): | |
if not self.version or self.version==4: | |
return uuid.uuid4() | |
elif self.version==1: | |
return uuid.uuid1(self.node, self.clock_seq) | |
elif self.version==2: | |
raise UUIDVersionError("UUID version 2 is not supported.") | |
elif self.version==3: | |
return uuid.uuid3(self.namespace, self.name) | |
elif self.version==5: | |
return uuid.uuid5(self.namespace, self.name) | |
else: | |
raise UUIDVersionError("UUID version %s is not valid." % self.version) | |
def db_type(self): | |
from django.conf import settings | |
return UUIDField._CREATE_COLUMN_TYPES.get(settings.DATABASE_ENGINE, "char(%s)" % self.max_length) | |
def to_python(self, value): | |
"""Return a uuid.UUID instance from the value returned by the database.""" | |
# | |
# This is the proper way... But this doesn't work correctly when | |
# working with an inherited model | |
# | |
if not value: | |
return None | |
if isinstance(value, uuid.UUID): | |
return value | |
# attempt to parse a UUID | |
return uuid.UUID(smart_unicode(value)) | |
# | |
# If I do the following (returning a String instead of a UUID | |
# instance), everything works. | |
# | |
#if not value: | |
# return None | |
#if isinstance(value, uuid.UUID): | |
# return smart_unicode(value) | |
#else: | |
# return value | |
def pre_save(self, model_instance, add): | |
if self.auto and add: | |
value = self.create_uuid() | |
setattr(model_instance, self.attname, value) | |
else: | |
value = super(UUIDField, self).pre_save(model_instance,add) | |
if self.auto and not value: | |
value = self.create_uuid() | |
setattr(model_instance, self.attname, value) | |
return value | |
def get_db_prep_value(self, value): | |
"""Casts uuid.UUID values into the format expected by the back end for use in queries""" | |
if isinstance(value, uuid.UUID): | |
return smart_unicode(value) | |
return value | |
def value_to_string(self, obj): | |
val = self._get_val_from_obj(obj) | |
if val is None: | |
data = '' | |
else: | |
data = smart_unicode(val) | |
return data | |
def formfield(self, **kwargs): | |
defaults = { | |
'form_class': forms.CharField, | |
'max_length': self.max_length | |
} | |
defaults.update(kwargs) | |
return super(UUIDField, self).formfield(**defaults) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The version of this code found in the django_polymorphic project seems to be compatible with newer Django versions. I suppose this is based on Gordon Tillman's work?