Repository: encode/django-rest-framework

Issue:
Multiple fields with the same source clobber each other
Hey guys, I'm getting weird behavior with many related fields. Is this an error?
## 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.
- [ ] 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

Create a model serializer where one field renders the relation, and the other field writes to it.

``` python
class UserSerializer(serializers.ModelSerializer):
    dogs = DogSerializer(many=True, read_only=True)
    dog_ids = serializers.PrimaryKeyRelatedField(source='dogs', many=True, queryset=Dog.objects.all())

    class Meta:
        fields = ('dogs', 'dog_ids',)
        model = User
```

Then do a patch.

```
PATCH /api/users/23 HTTP/1.1
Content-Type: application/json

{
    "dog_ids": [5, 9]
}
```
## Expected behavior

It should update my model.
## Actual behavior

It throws an error complaining that nested writes aren't allowed.

```
AssertionError at /api/users/23
The `.update()` method does not support writable nestedfields by default.
Write an explicit `.update()` method for serializer `app.serializers.DogSerializer`, or set `read_only=True` on nested serializer fields.
```
## tl;dr

This assertion gets called, but it checks all fields rather than just the writable ones:
https://github.com/tomchristie/django-rest-framework/blob/master/rest_framework/serializers.py#L721

``` python
def raise_errors_on_nested_writes(method_name, serializer, validated_data):
    assert not any(
        isinstance(field, BaseSerializer) and
        (key in validated_data) and
        isinstance(validated_data[key], (list, dict))
        for key, field in serializer.fields.items()
    ), (
        'The `.{method_name}()` method does not support writable nested'
        'fields by default.\nWrite an explicit `.{method_name}()` method for '
        'serializer `{module}.{class_name}`, or set `read_only=True` on '
        'nested serializer fields.'.format(
            method_name=method_name,
            module=serializer.__class__.__module__,
            class_name=serializer.__class__.__name__
        )
    )
```



Discussion:
Passing a list of ids to a PrimaryKeyRelatedField? Can you post what you are trying to achieve here?

Hey @aswinm, basically I want to be able to:
1. serialize a many-to-many field as nested objects
2. modify a many-to-many field by passing an array of ids to a "meta" field, in this case `dog_ids`
3. use the same field name as the django model in the serialization of the nested objects, in this case `dogs`

FYI, the following code works and accomplishes 1 and 2, but not 3.

``` python
class UserSerializer(serializers.ModelSerializer):
    dog_objects = DogSerializer(source='dogs', many=True, read_only=True)
    dog_ids = serializers.PrimaryKeyRelatedField(source='dogs', many=True, queryset=Dog.objects.all())

    class Meta:
        fields = ('dog_objects', 'dog_ids',)
        model = User
```

I can confirm the issue, the pull request that changed the behaviour of nested write check is this #4568 and in my humble opionion i would go for the extra 'read_only' check proposed by @educolo otherwise the issue will happen one way or another.

Having same exact problem.


Resolving patch:
diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py
--- a/rest_framework/serializers.py
+++ b/rest_framework/serializers.py
@@ -769,7 +769,7 @@ def raise_errors_on_nested_writes(method_name, serializer, validated_data):
         isinstance(field, BaseSerializer) and
         (field.source in validated_data) and
         isinstance(validated_data[field.source], (list, dict))
-        for key, field in serializer.fields.items()
+        for field in serializer._writable_fields
     ), (
         'The `.{method_name}()` method does not support writable nested '
         'fields by default.\nWrite an explicit `.{method_name}()` method for '


Test patch: