summaryrefslogtreecommitdiff
path: root/django
diff options
context:
space:
mode:
authorMarc Tamlyn <marc.tamlyn@gmail.com>2014-07-15 10:35:29 +0100
committerMarc Tamlyn <marc.tamlyn@gmail.com>2014-09-16 10:08:09 +0100
commited7821231b7dbf34a6c8ca65be3b9bcbda4a0703 (patch)
tree98657032a61094eb7ac9c46d5db5bc12d25d1fa0 /django
parent0d1561d19739e83cf20150585c9e26894b428bad (diff)
Fixed #19463 -- Added UUIDField
Uses native support in postgres, and char(32) on other backends.
Diffstat (limited to 'django')
-rw-r--r--django/db/backends/mysql/base.py8
-rw-r--r--django/db/backends/mysql/creation.py1
-rw-r--r--django/db/backends/oracle/base.py8
-rw-r--r--django/db/backends/oracle/creation.py1
-rw-r--r--django/db/backends/postgresql_psycopg2/base.py2
-rw-r--r--django/db/backends/postgresql_psycopg2/creation.py1
-rw-r--r--django/db/backends/sqlite3/base.py10
-rw-r--r--django/db/backends/sqlite3/creation.py1
-rw-r--r--django/db/models/fields/__init__.py43
-rw-r--r--django/forms/fields.py25
10 files changed, 98 insertions, 2 deletions
diff --git a/django/db/backends/mysql/base.py b/django/db/backends/mysql/base.py
index 69084aaa62..aefe4262e6 100644
--- a/django/db/backends/mysql/base.py
+++ b/django/db/backends/mysql/base.py
@@ -8,6 +8,7 @@ from __future__ import unicode_literals
import datetime
import re
import sys
+import uuid
import warnings
try:
@@ -398,6 +399,8 @@ class DatabaseOperations(BaseDatabaseOperations):
converters = super(DatabaseOperations, self).get_db_converters(internal_type)
if internal_type in ['BooleanField', 'NullBooleanField']:
converters.append(self.convert_booleanfield_value)
+ if internal_type == 'UUIDField':
+ converters.append(self.convert_uuidfield_value)
return converters
def convert_booleanfield_value(self, value, field):
@@ -405,6 +408,11 @@ class DatabaseOperations(BaseDatabaseOperations):
value = bool(value)
return value
+ def convert_uuidfield_value(self, value, field):
+ if value is not None:
+ value = uuid.UUID(value)
+ return value
+
class DatabaseWrapper(BaseDatabaseWrapper):
vendor = 'mysql'
diff --git a/django/db/backends/mysql/creation.py b/django/db/backends/mysql/creation.py
index 565b1fd9a1..fd3a595eb4 100644
--- a/django/db/backends/mysql/creation.py
+++ b/django/db/backends/mysql/creation.py
@@ -30,6 +30,7 @@ class DatabaseCreation(BaseDatabaseCreation):
'SmallIntegerField': 'smallint',
'TextField': 'longtext',
'TimeField': 'time',
+ 'UUIDField': 'char(32)',
}
def sql_table_creation_suffix(self):
diff --git a/django/db/backends/oracle/base.py b/django/db/backends/oracle/base.py
index 17457dfcea..e48c82f0d8 100644
--- a/django/db/backends/oracle/base.py
+++ b/django/db/backends/oracle/base.py
@@ -10,6 +10,7 @@ import decimal
import re
import platform
import sys
+import uuid
import warnings
@@ -264,6 +265,8 @@ WHEN (new.%(col_name)s IS NULL)
converters.append(self.convert_datefield_value)
elif internal_type == 'TimeField':
converters.append(self.convert_timefield_value)
+ elif internal_type == 'UUIDField':
+ converters.append(self.convert_uuidfield_value)
converters.append(self.convert_empty_values)
return converters
@@ -310,6 +313,11 @@ WHEN (new.%(col_name)s IS NULL)
value = value.time()
return value
+ def convert_uuidfield_value(self, value, field):
+ if value is not None:
+ value = uuid.UUID(value)
+ return value
+
def deferrable_sql(self):
return " DEFERRABLE INITIALLY DEFERRED"
diff --git a/django/db/backends/oracle/creation.py b/django/db/backends/oracle/creation.py
index 4b9146da7b..ef0156bc18 100644
--- a/django/db/backends/oracle/creation.py
+++ b/django/db/backends/oracle/creation.py
@@ -44,6 +44,7 @@ class DatabaseCreation(BaseDatabaseCreation):
'TextField': 'NCLOB',
'TimeField': 'TIMESTAMP',
'URLField': 'VARCHAR2(%(max_length)s)',
+ 'UUIDField': 'VARCHAR2(32)',
}
data_type_check_constraints = {
diff --git a/django/db/backends/postgresql_psycopg2/base.py b/django/db/backends/postgresql_psycopg2/base.py
index 4cd327a22b..975eee4df2 100644
--- a/django/db/backends/postgresql_psycopg2/base.py
+++ b/django/db/backends/postgresql_psycopg2/base.py
@@ -22,6 +22,7 @@ from django.utils.timezone import utc
try:
import psycopg2 as Database
import psycopg2.extensions
+ import psycopg2.extras
except ImportError as e:
from django.core.exceptions import ImproperlyConfigured
raise ImproperlyConfigured("Error loading psycopg2 module: %s" % e)
@@ -33,6 +34,7 @@ psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
psycopg2.extensions.register_type(psycopg2.extensions.UNICODEARRAY)
psycopg2.extensions.register_adapter(SafeBytes, psycopg2.extensions.QuotedString)
psycopg2.extensions.register_adapter(SafeText, psycopg2.extensions.QuotedString)
+psycopg2.extras.register_uuid()
def utc_tzinfo_factory(offset):
diff --git a/django/db/backends/postgresql_psycopg2/creation.py b/django/db/backends/postgresql_psycopg2/creation.py
index 6a190bf092..9573107271 100644
--- a/django/db/backends/postgresql_psycopg2/creation.py
+++ b/django/db/backends/postgresql_psycopg2/creation.py
@@ -31,6 +31,7 @@ class DatabaseCreation(BaseDatabaseCreation):
'SmallIntegerField': 'smallint',
'TextField': 'text',
'TimeField': 'time',
+ 'UUIDField': 'uuid',
}
data_type_check_constraints = {
diff --git a/django/db/backends/sqlite3/base.py b/django/db/backends/sqlite3/base.py
index 1da2542382..0e1070e6e5 100644
--- a/django/db/backends/sqlite3/base.py
+++ b/django/db/backends/sqlite3/base.py
@@ -8,8 +8,9 @@ from __future__ import unicode_literals
import datetime
import decimal
-import warnings
import re
+import uuid
+import warnings
from django.conf import settings
from django.db import utils
@@ -273,6 +274,8 @@ class DatabaseOperations(BaseDatabaseOperations):
converters.append(self.convert_timefield_value)
elif internal_type == 'DecimalField':
converters.append(self.convert_decimalfield_value)
+ elif internal_type == 'UUIDField':
+ converters.append(self.convert_uuidfield_value)
return converters
def convert_decimalfield_value(self, value, field):
@@ -295,6 +298,11 @@ class DatabaseOperations(BaseDatabaseOperations):
value = parse_time(value)
return value
+ def convert_uuidfield_value(self, value, field):
+ if value is not None:
+ value = uuid.UUID(value)
+ return value
+
def bulk_insert_sql(self, fields, num_values):
res = []
res.append("SELECT %s" % ", ".join(
diff --git a/django/db/backends/sqlite3/creation.py b/django/db/backends/sqlite3/creation.py
index 43b3924f4f..ea91c4a2b7 100644
--- a/django/db/backends/sqlite3/creation.py
+++ b/django/db/backends/sqlite3/creation.py
@@ -33,6 +33,7 @@ class DatabaseCreation(BaseDatabaseCreation):
'SmallIntegerField': 'smallint',
'TextField': 'text',
'TimeField': 'time',
+ 'UUIDField': 'char(32)',
}
data_types_suffix = {
'AutoField': 'AUTOINCREMENT',
diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py
index c41d4eaad2..de7631c246 100644
--- a/django/db/models/fields/__init__.py
+++ b/django/db/models/fields/__init__.py
@@ -6,6 +6,7 @@ import copy
import datetime
import decimal
import math
+import uuid
import warnings
from base64 import b64decode, b64encode
from itertools import tee
@@ -40,6 +41,7 @@ __all__ = [str(x) for x in (
'GenericIPAddressField', 'IPAddressField', 'IntegerField', 'NOT_PROVIDED',
'NullBooleanField', 'PositiveIntegerField', 'PositiveSmallIntegerField',
'SlugField', 'SmallIntegerField', 'TextField', 'TimeField', 'URLField',
+ 'UUIDField',
)]
@@ -2217,3 +2219,44 @@ class BinaryField(Field):
if isinstance(value, six.text_type):
return six.memoryview(b64decode(force_bytes(value)))
return value
+
+
+class UUIDField(Field):
+ default_error_messages = {
+ 'invalid': _("'%(value)s' is not a valid UUID."),
+ }
+ description = 'Universally unique identifier'
+ empty_strings_allowed = False
+
+ def __init__(self, **kwargs):
+ kwargs['max_length'] = 32
+ super(UUIDField, self).__init__(**kwargs)
+
+ def get_internal_type(self):
+ return "UUIDField"
+
+ def get_prep_value(self, value):
+ if isinstance(value, uuid.UUID):
+ return value.hex
+ if isinstance(value, six.string_types):
+ return value.replace('-', '')
+ return value
+
+ def to_python(self, value):
+ if value and not isinstance(value, uuid.UUID):
+ try:
+ return uuid.UUID(value)
+ except ValueError:
+ raise exceptions.ValidationError(
+ self.error_messages['invalid'],
+ code='invalid',
+ params={'value': value},
+ )
+ return value
+
+ def formfield(self, **kwargs):
+ defaults = {
+ 'form_class': forms.UUIDField,
+ }
+ defaults.update(kwargs)
+ return super(UUIDField, self).formfield(**defaults)
diff --git a/django/forms/fields.py b/django/forms/fields.py
index 2b44c73996..6403509fbb 100644
--- a/django/forms/fields.py
+++ b/django/forms/fields.py
@@ -9,6 +9,7 @@ import datetime
import os
import re
import sys
+import uuid
import warnings
from decimal import Decimal, DecimalException
from io import BytesIO
@@ -41,7 +42,7 @@ __all__ = (
'BooleanField', 'NullBooleanField', 'ChoiceField', 'MultipleChoiceField',
'ComboField', 'MultiValueField', 'FloatField', 'DecimalField',
'SplitDateTimeField', 'IPAddressField', 'GenericIPAddressField', 'FilePathField',
- 'SlugField', 'TypedChoiceField', 'TypedMultipleChoiceField'
+ 'SlugField', 'TypedChoiceField', 'TypedMultipleChoiceField', 'UUIDField',
)
@@ -1224,3 +1225,25 @@ class SlugField(CharField):
def clean(self, value):
value = self.to_python(value).strip()
return super(SlugField, self).clean(value)
+
+
+class UUIDField(CharField):
+ default_error_messages = {
+ 'invalid': _('Enter a valid UUID.'),
+ }
+
+ def prepare_value(self, value):
+ if isinstance(value, uuid.UUID):
+ return value.hex
+ return value
+
+ def to_python(self, value):
+ value = super(UUIDField, self).to_python(value)
+ if value in self.empty_values:
+ return None
+ if not isinstance(value, uuid.UUID):
+ try:
+ value = uuid.UUID(value)
+ except ValueError:
+ raise ValidationError(self.error_messages['invalid'], code='invalid')
+ return value