Repository: encode/django-rest-framework

Issue:
Custom model fields may map to serializer fields incorrectly.
I just upgraded to 2.4.0 and I stumbled upon an issue regarding field serialization.

I have a custom field on a model that is a subclass of a Django **CharField**, and when instantiate the ModelSerializer based on this model, I encountered an error running this code (in the [_get_field_](https://github.com/tomchristie/django-rest-framework/blob/2.4.0/rest_framework/serializers.py#L851) method of **ModelSerializer**) :

``` python
if model_field.null and \
        issubclass(model_field.__class__, (models.CharField, models.TextField)):
    kwargs['allow_none'] = True

attribute_dict = {
    models.CharField: ['max_length'],
    models.CommaSeparatedIntegerField: ['max_length'],
    models.DecimalField: ['max_digits', 'decimal_places'],
    models.EmailField: ['max_length'],
    models.FileField: ['max_length'],
    models.ImageField: ['max_length'],
    models.SlugField: ['max_length'],
    models.URLField: ['max_length'],
}

if model_field.__class__ in attribute_dict:
    attributes = attribute_dict[model_field.__class__]
    for attribute in attributes:
        kwargs.update({attribute: getattr(model_field, attribute)})

try:
    return self.field_mapping[model_field.__class__](**kwargs)
except KeyError:
    return ModelField(model_field=model_field, **kwargs)
```

My field is a subclass of **CharField** so the kwargs do get an _allow_none_ key, but since it is not in the _attribute_dict_, I get the KeyError exception, and since the ModelField constructor doesn't allow an _allow_none_ argument, I'm screwed.

I haven't submitted any code since I don't know which direction to take (yet) but some comments about these suggestions might help:
- Should the framework handle this case?
  If it does:
- Since IMO the test if the field is a subclass of a **Charfield** was meant for **SlugField**, **EmailField**, etc., maybe adding a membership test on the attribute_dict would prevent the _allow_none_ key to be inserted?
- Or, when the **KeyError** exception is thrown, using the same subclass test, we could instantiate a **CharField** rather than a **ModelField**.

Cheers



Discussion:

Resolving patch:
diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py
--- a/rest_framework/serializers.py
+++ b/rest_framework/serializers.py
@@ -625,6 +625,20 @@ def __init__(self, meta):
         self.write_only_fields = getattr(meta, 'write_only_fields', ())
 
 
+def _get_class_mapping(mapping, obj):
+    """
+    Takes a dictionary with classes as keys, and an object.
+    Traverses the object's inheritance hierarchy in method
+    resolution order, and returns the first matching value
+    from the dictionary or None.
+
+    """
+    return next(
+        (mapping[cls] for cls in inspect.getmro(obj.__class__) if cls in mapping),
+        None
+    )
+
+
 class ModelSerializer(Serializer):
     """
     A serializer that deals with model instances and querysets.
@@ -899,15 +913,17 @@ def get_field(self, model_field):
             models.URLField: ['max_length'],
         }
 
-        if model_field.__class__ in attribute_dict:
-            attributes = attribute_dict[model_field.__class__]
+        attributes = _get_class_mapping(attribute_dict, model_field)
+        if attributes:
             for attribute in attributes:
                 kwargs.update({attribute: getattr(model_field, attribute)})
 
-        try:
-            return self.field_mapping[model_field.__class__](**kwargs)
-        except KeyError:
-            return ModelField(model_field=model_field, **kwargs)
+        serializer_field_class = _get_class_mapping(
+            self.field_mapping, model_field)
+
+        if serializer_field_class:
+            return serializer_field_class(**kwargs)
+        return ModelField(model_field=model_field, **kwargs)
 
     def get_validation_exclusions(self, instance=None):
         """


Test patch: