Repository: encode/django-rest-framework

Issue:
Support 1.8's UUIDField
As per comment here: https://github.com/tomchristie/django-rest-framework/issues/2426#issuecomment-70484022

Current str field would result in...

```
File "/Users/tomjaster/DevFolder/master/airyengine/airy/lib/rest_framework/utils/encoders.py", line 60, in default
    return super(JSONEncoder, self).default(obj)
File "/usr/local/Cellar/python/2.7.8_1/Frameworks/Python.framework/Versions/2.7/lib/python2.7/json/encoder.py", line 184, in default
    raise TypeError(repr(o) + " is not JSON serializable")
    TypeError: UUID('335680b8-0f20-4e25-a0e2-3caa052a3e83') is not JSON serializable
```

We should validate UUID values, and handle str() of them in both the encoder, and in the serializer field.



Discussion:

Resolving patch:
diff --git a/rest_framework/fields.py b/rest_framework/fields.py
--- a/rest_framework/fields.py
+++ b/rest_framework/fields.py
@@ -23,6 +23,7 @@
 import decimal
 import inspect
 import re
+import uuid
 
 
 class empty:
@@ -632,6 +633,23 @@ def __init__(self, **kwargs):
         self.validators.append(validator)
 
 
+class UUIDField(Field):
+    default_error_messages = {
+        'invalid': _('"{value}" is not a valid UUID.'),
+    }
+
+    def to_internal_value(self, data):
+        if not isinstance(data, uuid.UUID):
+            try:
+                return uuid.UUID(data)
+            except (ValueError, TypeError):
+                self.fail('invalid', value=data)
+        return data
+
+    def to_representation(self, value):
+        return str(value)
+
+
 # Number types...
 
 class IntegerField(Field):
diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py
--- a/rest_framework/serializers.py
+++ b/rest_framework/serializers.py
@@ -702,6 +702,7 @@ class ModelSerializer(Serializer):
     you need you should either declare the extra/differing fields explicitly on
     the serializer class, or simply use a `Serializer` class.
     """
+
     _field_mapping = ClassLookupDict({
         models.AutoField: IntegerField,
         models.BigIntegerField: IntegerField,
@@ -724,7 +725,8 @@ class ModelSerializer(Serializer):
         models.SmallIntegerField: IntegerField,
         models.TextField: CharField,
         models.TimeField: TimeField,
-        models.URLField: URLField,
+        models.URLField: URLField
+        # Note: Some version-specific mappings also defined below.
     })
     _related_class = PrimaryKeyRelatedField
 
@@ -1132,6 +1134,10 @@ class Meta:
         return NestedSerializer
 
 
+if hasattr(models, 'UUIDField'):
+    ModelSerializer._field_mapping[models.UUIDField] = UUIDField
+
+
 class HyperlinkedModelSerializer(ModelSerializer):
     """
     A type of `ModelSerializer` that uses hyperlinked relationships instead
diff --git a/rest_framework/utils/field_mapping.py b/rest_framework/utils/field_mapping.py
--- a/rest_framework/utils/field_mapping.py
+++ b/rest_framework/utils/field_mapping.py
@@ -38,6 +38,9 @@ def __getitem__(self, key):
                 return self.mapping[cls]
         raise KeyError('Class %s not found in lookup.', cls.__name__)
 
+    def __setitem__(self, key, value):
+        self.mapping[key] = value
+
 
 def needs_label(model_field, field_name):
     """


Test patch:
diff --git a/tests/test_fields.py b/tests/test_fields.py
--- a/tests/test_fields.py
+++ b/tests/test_fields.py
@@ -4,6 +4,7 @@
 import datetime
 import django
 import pytest
+import uuid
 
 
 # Tests for field keyword arguments and core functionality.
@@ -467,6 +468,23 @@ class TestURLField(FieldValues):
     field = serializers.URLField()
 
 
+class TestUUIDField(FieldValues):
+    """
+    Valid and invalid values for `UUIDField`.
+    """
+    valid_inputs = {
+        '825d7aeb-05a9-45b5-a5b7-05df87923cda': uuid.UUID('825d7aeb-05a9-45b5-a5b7-05df87923cda'),
+        '825d7aeb05a945b5a5b705df87923cda': uuid.UUID('825d7aeb-05a9-45b5-a5b7-05df87923cda')
+    }
+    invalid_inputs = {
+        '825d7aeb-05a9-45b5-a5b7': ['"825d7aeb-05a9-45b5-a5b7" is not a valid UUID.']
+    }
+    outputs = {
+        uuid.UUID('825d7aeb-05a9-45b5-a5b7-05df87923cda'): '825d7aeb-05a9-45b5-a5b7-05df87923cda'
+    }
+    field = serializers.UUIDField()
+
+
 # Number types...
 
 class TestIntegerField(FieldValues):