146 lines
5.1 KiB
Python
146 lines
5.1 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
####################################################################
|
|
|
|
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)
|