From e8db1834d3a3f6ba05276b64e5681288aa8f9820 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 23 Jan 2015 15:24:06 +0000 Subject: [PATCH 1/3] Added UUIDField. --- rest_framework/fields.py | 18 ++++++++++++++++++ rest_framework/serializers.py | 8 +++++++- tests/test_fields.py | 18 ++++++++++++++++++ 3 files changed, 43 insertions(+), 1 deletion(-) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index cc9410aa7e..5e3f7ce4f3 100644 --- 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 index cf797bdcb5..dca612cad0 100644 --- 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/tests/test_fields.py b/tests/test_fields.py index 775d461848..a46cc2052e 100644 --- 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): From 5bb348605e5dad3b58495b1fc56ea393194b89fb Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 23 Jan 2015 15:31:08 +0000 Subject: [PATCH 2/3] UUIDField docs --- docs/api-guide/fields.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md index b3d274ddb7..64ec902b47 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -182,6 +182,12 @@ Corresponds to `django.db.models.fields.URLField`. Uses Django's `django.core.v **Signature:** `URLField(max_length=200, min_length=None, allow_blank=False)` +## UUIDField + +A field that ensures the input is a valid UUID string. The `to_internal_value` method will return a `uuid.UUID` instance. On output the field will return a string in the canonical hyphenated format, for example: + + "de305d54-75b4-431b-adb2-eb6b9e546013" + --- # Numeric fields @@ -320,7 +326,7 @@ Both the `allow_blank` and `allow_null` are valid options on `ChoiceField`, alth ## MultipleChoiceField -A field that can accept a set of zero, one or many values, chosen from a limited set of choices. Takes a single mandatory argument. `to_internal_representation` returns a `set` containing the selected values. +A field that can accept a set of zero, one or many values, chosen from a limited set of choices. Takes a single mandatory argument. `to_internal_value` returns a `set` containing the selected values. **Signature:** `MultipleChoiceField(choices)` From 889a07f5563a0f970639a0958c0dcbc26e82919f Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 23 Jan 2015 15:32:21 +0000 Subject: [PATCH 3/3] Support assignment in ClassLookupDict --- rest_framework/utils/field_mapping.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/rest_framework/utils/field_mapping.py b/rest_framework/utils/field_mapping.py index cba40d3184..c97ec5d0ed 100644 --- 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): """