diff options
Diffstat (limited to 'rest_framework')
| -rw-r--r-- | rest_framework/__init__.py | 2 | ||||
| -rw-r--r-- | rest_framework/authtoken/south_migrations/0001_initial.py | 2 | ||||
| -rw-r--r-- | rest_framework/compat.py | 7 | ||||
| -rw-r--r-- | rest_framework/fields.py | 77 | ||||
| -rw-r--r-- | rest_framework/parsers.py | 2 | ||||
| -rw-r--r-- | rest_framework/relations.py | 5 | ||||
| -rw-r--r-- | rest_framework/routers.py | 10 | ||||
| -rw-r--r-- | rest_framework/serializers.py | 12 | ||||
| -rw-r--r-- | rest_framework/settings.py | 11 | ||||
| -rw-r--r-- | rest_framework/utils/field_mapping.py | 3 | 
10 files changed, 117 insertions, 14 deletions
| diff --git a/rest_framework/__init__.py b/rest_framework/__init__.py index fdcebb7b..57e5421b 100644 --- a/rest_framework/__init__.py +++ b/rest_framework/__init__.py @@ -8,7 +8,7 @@ ______ _____ _____ _____    __  """  __title__ = 'Django REST framework' -__version__ = '3.0.3' +__version__ = '3.0.4'  __author__ = 'Tom Christie'  __license__ = 'BSD 2-Clause'  __copyright__ = 'Copyright 2011-2015 Tom Christie' diff --git a/rest_framework/authtoken/south_migrations/0001_initial.py b/rest_framework/authtoken/south_migrations/0001_initial.py index 926de02b..5b927f3e 100644 --- a/rest_framework/authtoken/south_migrations/0001_initial.py +++ b/rest_framework/authtoken/south_migrations/0001_initial.py @@ -40,7 +40,7 @@ class Migration(SchemaMigration):              'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})          },          "%s.%s" % (User._meta.app_label, User._meta.module_name): { -            'Meta': {'object_name': User._meta.module_name}, +            'Meta': {'object_name': User._meta.module_name, 'db_table': repr(User._meta.db_table)},          },          'authtoken.token': {              'Meta': {'object_name': 'Token'}, diff --git a/rest_framework/compat.py b/rest_framework/compat.py index a6620057..50f37014 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -63,6 +63,13 @@ except ImportError:      from django.http import HttpResponse as HttpResponseBase +# contrib.postgres only supported from 1.8 onwards. +try: +    from django.contrib.postgres import fields as postgres_fields +except ImportError: +    postgres_fields = None + +  # request only provides `resolver_match` from 1.5 onwards.  def get_resolver_match(request):      try: diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 342564d3..02d2adef 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -23,6 +23,7 @@ import datetime  import decimal  import inspect  import re +import uuid  class empty: @@ -632,6 +633,23 @@ class URLField(CharField):          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): @@ -1113,8 +1131,21 @@ class ImageField(FileField):  # Composite field types... +class _UnvalidatedField(Field): +    def __init__(self, *args, **kwargs): +        super(_UnvalidatedField, self).__init__(*args, **kwargs) +        self.allow_blank = True +        self.allow_null = True + +    def to_internal_value(self, data): +        return data + +    def to_representation(self, value): +        return value + +  class ListField(Field): -    child = None +    child = _UnvalidatedField()      initial = []      default_error_messages = {          'not_a_list': _('Expected a list of items but got type "{input_type}".') @@ -1122,7 +1153,6 @@ class ListField(Field):      def __init__(self, *args, **kwargs):          self.child = kwargs.pop('child', copy.deepcopy(self.child)) -        assert self.child is not None, '`child` is a required argument.'          assert not inspect.isclass(self.child), '`child` has not been instantiated.'          super(ListField, self).__init__(*args, **kwargs)          self.child.bind(field_name='', parent=self) @@ -1151,6 +1181,49 @@ class ListField(Field):          return [self.child.to_representation(item) for item in data] +class DictField(Field): +    child = _UnvalidatedField() +    initial = [] +    default_error_messages = { +        'not_a_dict': _('Expected a dictionary of items but got type "{input_type}".') +    } + +    def __init__(self, *args, **kwargs): +        self.child = kwargs.pop('child', copy.deepcopy(self.child)) +        assert not inspect.isclass(self.child), '`child` has not been instantiated.' +        super(DictField, self).__init__(*args, **kwargs) +        self.child.bind(field_name='', parent=self) + +    def get_value(self, dictionary): +        # We override the default field access in order to support +        # lists in HTML forms. +        if html.is_html_input(dictionary): +            return html.parse_html_list(dictionary, prefix=self.field_name) +        return dictionary.get(self.field_name, empty) + +    def to_internal_value(self, data): +        """ +        Dicts of native values <- Dicts of primitive datatypes. +        """ +        if html.is_html_input(data): +            data = html.parse_html_dict(data) +        if not isinstance(data, dict): +            self.fail('not_a_dict', input_type=type(data).__name__) +        return dict([ +            (six.text_type(key), self.child.run_validation(value)) +            for key, value in data.items() +        ]) + +    def to_representation(self, value): +        """ +        List of object instances -> List of dicts of primitive datatypes. +        """ +        return dict([ +            (six.text_type(key), self.child.to_representation(val)) +            for key, val in value.items() +        ]) + +  # Miscellaneous field types...  class ReadOnlyField(Field): diff --git a/rest_framework/parsers.py b/rest_framework/parsers.py index 7a51bb05..437d1339 100644 --- a/rest_framework/parsers.py +++ b/rest_framework/parsers.py @@ -152,7 +152,7 @@ class FileUploadParser(BaseParser):                                                None,                                                encoding)              if result is not None: -                return DataAndFiles(None, {'file': result[1]}) +                return DataAndFiles({}, {'file': result[1]})          # This is the standard case.          possible_sizes = [x.chunk_size for x in upload_handlers if x.chunk_size] diff --git a/rest_framework/relations.py b/rest_framework/relations.py index a85edfec..66857a41 100644 --- a/rest_framework/relations.py +++ b/rest_framework/relations.py @@ -338,7 +338,12 @@ class ManyRelatedField(Field):          # We override the default field access in order to support          # lists in HTML forms.          if html.is_html_input(dictionary): +            # Don't return [] if the update is partial +            if self.field_name not in dictionary: +                if getattr(self.root, 'partial', False): +                    return empty              return dictionary.getlist(self.field_name) +          return dictionary.get(self.field_name, empty)      def to_internal_value(self, data): diff --git a/rest_framework/routers.py b/rest_framework/routers.py index 827da034..6a4184e2 100644 --- a/rest_framework/routers.py +++ b/rest_framework/routers.py @@ -130,19 +130,13 @@ class SimpleRouter(BaseRouter):          If `base_name` is not specified, attempt to automatically determine          it from the viewset.          """ -        # Note that `.model` attribute on views is deprecated, although we -        # enforce the deprecation on the view `get_serializer_class()` and -        # `get_queryset()` methods, rather than here. -        model_cls = getattr(viewset, 'model', None)          queryset = getattr(viewset, 'queryset', None) -        if model_cls is None and queryset is not None: -            model_cls = queryset.model -        assert model_cls, '`base_name` argument not specified, and could ' \ +        assert queryset is not None, '`base_name` argument not specified, and could ' \              'not automatically determine the name from the viewset, as ' \              'it does not have a `.queryset` attribute.' -        return model_cls._meta.object_name.lower() +        return queryset.model._meta.object_name.lower()      def get_routes(self, viewset):          """ diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index d9a67441..a3b8196b 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -14,7 +14,7 @@ from __future__ import unicode_literals  from django.db import models  from django.db.models.fields import FieldDoesNotExist, Field as DjangoModelField  from django.utils.translation import ugettext_lazy as _ -from rest_framework.compat import unicode_to_repr +from rest_framework.compat import postgres_fields, unicode_to_repr  from rest_framework.utils import model_meta  from rest_framework.utils.field_mapping import (      get_url_kwargs, get_field_kwargs, @@ -1329,6 +1329,16 @@ class ModelSerializer(Serializer):          return validators +if hasattr(models, 'UUIDField'): +    ModelSerializer._field_mapping[models.UUIDField] = UUIDField + +if postgres_fields: +    class CharMappingField(DictField): +        child = CharField() + +    ModelSerializer._field_mapping[postgres_fields.HStoreField] = CharMappingField + +  class HyperlinkedModelSerializer(ModelSerializer):      """      A type of `ModelSerializer` that uses hyperlinked relationships instead diff --git a/rest_framework/settings.py b/rest_framework/settings.py index ca5af86e..7331f265 100644 --- a/rest_framework/settings.py +++ b/rest_framework/settings.py @@ -18,6 +18,7 @@ REST framework settings, checking for user settings first, then falling  back to the defaults.  """  from __future__ import unicode_literals +from django.test.signals import setting_changed  from django.conf import settings  from django.utils import importlib, six  from rest_framework import ISO_8601 @@ -207,3 +208,13 @@ class APISettings(object):  api_settings = APISettings(USER_SETTINGS, DEFAULTS, IMPORT_STRINGS) + + +def reload_api_settings(*args, **kwargs): +    global api_settings +    setting, value = kwargs['setting'], kwargs['value'] +    if setting == 'REST_FRAMEWORK': +        api_settings = APISettings(value, DEFAULTS, IMPORT_STRINGS) + + +setting_changed.connect(reload_api_settings) diff --git a/rest_framework/utils/field_mapping.py b/rest_framework/utils/field_mapping.py index cba40d31..c97ec5d0 100644 --- a/rest_framework/utils/field_mapping.py +++ b/rest_framework/utils/field_mapping.py @@ -38,6 +38,9 @@ class ClassLookupDict(object):                  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):      """ | 
