diff options
| author | Tom Christie | 2014-10-02 16:24:24 +0100 |
|---|---|---|
| committer | Tom Christie | 2014-10-02 16:24:24 +0100 |
| commit | df7b6fcf58417fd95e49655eb140b387899b1ceb (patch) | |
| tree | a4a7d932bb3ef7c8e326b0248662fd31edcc2658 /rest_framework/serializers.py | |
| parent | ffc6aa3abcb0f823b43b63db1666913565e6f934 (diff) | |
| download | django-rest-framework-df7b6fcf58417fd95e49655eb140b387899b1ceb.tar.bz2 | |
First pass on incorperating the form rendering into the browsable API
Diffstat (limited to 'rest_framework/serializers.py')
| -rw-r--r-- | rest_framework/serializers.py | 129 |
1 files changed, 92 insertions, 37 deletions
diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 5da81247..0f24ed40 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -14,7 +14,6 @@ from django.core.exceptions import ImproperlyConfigured, ValidationError from django.db import models from django.utils import six from django.utils.datastructures import SortedDict -from collections import namedtuple from rest_framework.fields import empty, set_value, Field, SkipField from rest_framework.settings import api_settings from rest_framework.utils import html, model_meta, representation @@ -38,8 +37,8 @@ from rest_framework.relations import * # NOQA from rest_framework.fields import * # NOQA -FieldResult = namedtuple('FieldResult', ['field', 'value', 'error']) - +# BaseSerializer +# -------------- class BaseSerializer(Field): """ @@ -113,11 +112,6 @@ class BaseSerializer(Field): if not hasattr(self, '_data'): if self.instance is not None: self._data = self.to_representation(self.instance) - elif self._initial_data is not None: - self._data = dict([ - (field_name, field.get_value(self._initial_data)) - for field_name, field in self.fields.items() - ]) else: self._data = self.get_initial() return self._data @@ -137,34 +131,48 @@ class BaseSerializer(Field): return self._validated_data -class SerializerMetaclass(type): +# Serializer & ListSerializer classes +# ----------------------------------- + +class ReturnDict(SortedDict): """ - This metaclass sets a dictionary named `base_fields` on the class. + Return object from `serialier.data` for the `Serializer` class. + Includes a backlink to the serializer instance for renderers + to use if they need richer field information. + """ + def __init__(self, *args, **kwargs): + self.serializer = kwargs.pop('serializer') + super(ReturnDict, self).__init__(*args, **kwargs) - Any instances of `Field` included as attributes on either the class - or on any of its superclasses will be include in the - `base_fields` dictionary. + +class ReturnList(list): + """ + Return object from `serialier.data` for the `SerializerList` class. + Includes a backlink to the serializer instance for renderers + to use if they need richer field information. """ + def __init__(self, *args, **kwargs): + self.serializer = kwargs.pop('serializer') + super(ReturnList, self).__init__(*args, **kwargs) - @classmethod - def _get_declared_fields(cls, bases, attrs): - fields = [(field_name, attrs.pop(field_name)) - for field_name, obj in list(attrs.items()) - if isinstance(obj, Field)] - fields.sort(key=lambda x: x[1]._creation_counter) - # If this class is subclassing another Serializer, add that Serializer's - # fields. Note that we loop over the bases in *reverse*. This is necessary - # in order to maintain the correct order of fields. - for base in bases[::-1]: - if hasattr(base, '_declared_fields'): - fields = list(base._declared_fields.items()) + fields +class BoundField(object): + """ + A field object that also includes `.value` and `.error` properties. + Returned when iterating over a serializer instance, + providing an API similar to Django forms and form fields. + """ + def __init__(self, field, value, errors): + self._field = field + self.value = value + self.errors = errors - return SortedDict(fields) + def __getattr__(self, attr_name): + return getattr(self._field, attr_name) - def __new__(cls, name, bases, attrs): - attrs['_declared_fields'] = cls._get_declared_fields(bases, attrs) - return super(SerializerMetaclass, cls).__new__(cls, name, bases, attrs) + @property + def _proxy_class(self): + return self._field.__class__ class BindingDict(object): @@ -196,6 +204,36 @@ class BindingDict(object): return self.fields.values() +class SerializerMetaclass(type): + """ + This metaclass sets a dictionary named `base_fields` on the class. + + Any instances of `Field` included as attributes on either the class + or on any of its superclasses will be include in the + `base_fields` dictionary. + """ + + @classmethod + def _get_declared_fields(cls, bases, attrs): + fields = [(field_name, attrs.pop(field_name)) + for field_name, obj in list(attrs.items()) + if isinstance(obj, Field)] + fields.sort(key=lambda x: x[1]._creation_counter) + + # If this class is subclassing another Serializer, add that Serializer's + # fields. Note that we loop over the bases in *reverse*. This is necessary + # in order to maintain the correct order of fields. + for base in bases[::-1]: + if hasattr(base, '_declared_fields'): + fields = list(base._declared_fields.items()) + fields + + return SortedDict(fields) + + def __new__(cls, name, bases, attrs): + attrs['_declared_fields'] = cls._get_declared_fields(bases, attrs) + return super(SerializerMetaclass, cls).__new__(cls, name, bases, attrs) + + @six.add_metaclass(SerializerMetaclass) class Serializer(BaseSerializer): def __init__(self, *args, **kwargs): @@ -212,10 +250,18 @@ class Serializer(BaseSerializer): return copy.deepcopy(self._declared_fields) def get_initial(self): - return dict([ + if self._initial_data is not None: + return ReturnDict([ + (field_name, field.get_value(self._initial_data)) + for field_name, field in self.fields.items() + ], serializer=self) + #return self.to_representation(self._initial_data) + + return ReturnDict([ (field.field_name, field.get_initial()) for field in self.fields.values() - ]) + if not field.write_only + ], serializer=self) def get_value(self, dictionary): # We override the default field access in order to support @@ -288,7 +334,7 @@ class Serializer(BaseSerializer): """ Object instance -> Dict of primitive datatypes. """ - ret = SortedDict() + ret = ReturnDict(serializer=self) fields = [field for field in self.fields.values() if not field.write_only] for field in fields: @@ -302,11 +348,9 @@ class Serializer(BaseSerializer): def __iter__(self): errors = self.errors if hasattr(self, '_errors') else {} for field in self.fields.values(): - if field.read_only: - continue value = self.data.get(field.field_name) if self.data else None error = errors.get(field.field_name) - yield FieldResult(field, value, error) + yield BoundField(field, value, error) def __repr__(self): return representation.serializer_repr(self, indent=1) @@ -317,7 +361,7 @@ class Serializer(BaseSerializer): class ListSerializer(BaseSerializer): child = None - initial = [] + many = True def __init__(self, *args, **kwargs): self.child = kwargs.pop('child', copy.deepcopy(self.child)) @@ -326,6 +370,11 @@ class ListSerializer(BaseSerializer): super(ListSerializer, self).__init__(*args, **kwargs) self.child.bind(field_name='', parent=self) + def get_initial(self): + if self._initial_data is not None: + return self.to_representation(self._initial_data) + return ReturnList(serializer=self) + def get_value(self, dictionary): # We override the default field access in order to support # lists in HTML forms. @@ -345,7 +394,10 @@ class ListSerializer(BaseSerializer): """ List of object instances -> List of dicts of primitive datatypes. """ - return [self.child.to_representation(item) for item in data] + return ReturnList( + [self.child.to_representation(item) for item in data], + serializer=self + ) def create(self, attrs_list): return [self.child.create(attrs) for attrs in attrs_list] @@ -354,6 +406,9 @@ class ListSerializer(BaseSerializer): return representation.list_repr(self, indent=1) +# ModelSerializer & HyperlinkedModelSerializer +# -------------------------------------------- + class ModelSerializer(Serializer): _field_mapping = ClassLookupDict({ models.AutoField: IntegerField, |
