diff options
| author | Marc Tamlyn <marc.tamlyn@gmail.com> | 2014-07-15 10:35:29 +0100 |
|---|---|---|
| committer | Marc Tamlyn <marc.tamlyn@gmail.com> | 2014-09-16 10:08:09 +0100 |
| commit | ed7821231b7dbf34a6c8ca65be3b9bcbda4a0703 (patch) | |
| tree | 98657032a61094eb7ac9c46d5db5bc12d25d1fa0 /django | |
| parent | 0d1561d19739e83cf20150585c9e26894b428bad (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.py | 8 | ||||
| -rw-r--r-- | django/db/backends/mysql/creation.py | 1 | ||||
| -rw-r--r-- | django/db/backends/oracle/base.py | 8 | ||||
| -rw-r--r-- | django/db/backends/oracle/creation.py | 1 | ||||
| -rw-r--r-- | django/db/backends/postgresql_psycopg2/base.py | 2 | ||||
| -rw-r--r-- | django/db/backends/postgresql_psycopg2/creation.py | 1 | ||||
| -rw-r--r-- | django/db/backends/sqlite3/base.py | 10 | ||||
| -rw-r--r-- | django/db/backends/sqlite3/creation.py | 1 | ||||
| -rw-r--r-- | django/db/models/fields/__init__.py | 43 | ||||
| -rw-r--r-- | django/forms/fields.py | 25 |
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 |
