1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
|
# -*- coding: utf-8 -*-
####################################################################
import uuid
from django import forms
from django.db import models
from django.utils.encoding import smart_text
from django.utils import six
class UUIDVersionError(Exception):
pass
class UUIDField(six.with_metaclass(models.SubfieldBase, 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.
"""
_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, connection):
from django.conf import settings
full_database_type = settings.DATABASES['default']['ENGINE']
database_type = full_database_type.split('.')[-1]
return UUIDField._CREATE_COLUMN_TYPES.get(database_type, "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_text(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_text(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, connection, prepared):
"""Casts uuid.UUID values into the format expected by the back end for use in queries"""
if isinstance(value, uuid.UUID):
return smart_text(value)
return value
def value_to_string(self, obj):
val = self._get_val_from_obj(obj)
if val is None:
data = ''
else:
data = smart_text(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)
|