Repository: encode/django-rest-framework

Issue:
ModelSerializer exclude won't work well when field duplicate declear in Serializer
## Checklist

- [x] I have verified that that issue exists against the `master` branch of Django REST framework.
- [x] I have searched for similar issues in both open and closed tickets and cannot find a duplicate.
- [x] This is not a usage question. (Those should be directed to the [discussion group](https://groups.google.com/forum/#!forum/django-rest-framework) instead.)
- [x] This cannot be dealt with as a third party library. (We prefer new functionality to be [in the form of third party libraries](http://www.django-rest-framework.org/topics/third-party-resources/#about-third-party-packages) where possible.)
- [x] I have reduced the issue to the simplest possible case.
- [x] I have included a failing test as a pull request. (If you are unable to do so we can still accept the issue.)

## Steps to reproduce
I found that when I use ModelSerializer, if I set a field in ModelSerializer which is duplicate in model. Then ModelSerializer 's exclude attribute won't work well. It still return the exclude fields.

I traced down the code, and found that it failed because `ModelSerializer`.`get_field_names` exclude fields by using list. Since I have set the field in the ModelSerializer, there are two fields name in the list returned by `ModelSerializer`.`get_default_field_names`. 

django model I except to have
```python
class Car(models.Model):
    name = models.CharField(max_length=128)
    username = models.CharField(max_length=128)
```
serializar I have
```python
class VersionModelSerializer(serializers.ModelSerializer):
    username = fields.IntegerField(validators=my_validator)

    class Meta:
        model = models.Car
        exclude = ('username')

```

## Expected behavior
Excepted to return data like 

```
{
    "name": "car_name"
}
```

## Actual behavior
but username exists in the response like 
```
{
    "name": "car_name",
    "username": "username"
}
```

according to the reason I explain above, I think `ModelSerializer`.`get_field_names` should use `set` instead of `list` to exclude fields.




Discussion:
In such case, DRF should fail hard and refuse to start.
From the documentation:

> The names in the fields and exclude attributes will normally map to model fields on the model class.
> Alternatively names in the fields options can map to properties or methods which take no arguments that exist on the model class.

So I'm in favor of closing the issue as invalid since the documentation implies `exclude` fields are relating to model's and not those explicitly defined in the serializer. 
I’m with @xordoquy here. 

I look at the example serialiser and think “why would you explicitly define a field you’re going to exclude?” No doubt there’s some reuse scenario in play but it’s not the intended use: exclude is secondary to fields, just for some quick cases, fields is to be preferred, and fields requires including defined fields. (Thus this is a feature we can live with.) 

Resolving patch:
diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py
--- a/rest_framework/serializers.py
+++ b/rest_framework/serializers.py
@@ -1102,6 +1102,17 @@ def get_field_names(self, declared_fields, info):
         if exclude is not None:
             # If `Meta.exclude` is included, then remove those fields.
             for field_name in exclude:
+                assert field_name not in self._declared_fields, (
+                    "Cannot both declare the field '{field_name}' and include "
+                    "it in the {serializer_class} 'exclude' option. Remove the "
+                    "field or, if inherited from a parent serializer, disable "
+                    "with `{field_name} = None`."
+                    .format(
+                        field_name=field_name,
+                        serializer_class=self.__class__.__name__
+                    )
+                )
+
                 assert field_name in fields, (
                     "The field '{field_name}' was included on serializer "
                     "{serializer_class} in the 'exclude' option, but does "


Test patch:
diff --git a/tests/test_model_serializer.py b/tests/test_model_serializer.py
--- a/tests/test_model_serializer.py
+++ b/tests/test_model_serializer.py
@@ -900,6 +900,22 @@ class Meta:
             "Cannot set both 'fields' and 'exclude' options on serializer ExampleSerializer."
         )
 
+    def test_declared_fields_with_exclude_option(self):
+        class ExampleSerializer(serializers.ModelSerializer):
+            text = serializers.CharField()
+
+            class Meta:
+                model = MetaClassTestModel
+                exclude = ('text',)
+
+        expected = (
+            "Cannot both declare the field 'text' and include it in the "
+            "ExampleSerializer 'exclude' option. Remove the field or, if "
+            "inherited from a parent serializer, disable with `text = None`."
+        )
+        with self.assertRaisesMessage(AssertionError, expected):
+            ExampleSerializer().fields
+
 
 class Issue2704TestCase(TestCase):
     def test_queryset_all(self):