Combination of many=True and a dotted source doesn't allow a default · Issue #7550 · encode/django-rest-framework (original) (raw)

Checklist

Steps to reproduce

Consider a model with a many-many relationship to itself via a through model.

class FooModel(models.Model): text = models.CharField(max_length=100) bar = models.ForeignKey( 'BarModel', null=True, blank=True, on_delete=models.SET_NULL, related_name='foos', related_query_name='foo')

class BarModel(models.Model): pass

We can attempt to serialize this using something like the following.

    class _FooSerializer(serializers.ModelSerializer):
        class Meta:
            model = FooModel
            fields = ('id', 'text')

    class FooSerializer(serializers.ModelSerializer):
        other_foos = _FooSerializer(source='bar.foos', many=True)

        class Meta:
            model = FooModel
            fields = ('id', 'other_foos')

(NOTE: I haven't tested this yet. My case is a lot more complicated so I'll try reduce that is this reproducer isn't valid)

Expected behavior

This is intended to flatten the default output from:

{ "id": 1, "bar": { "foos": [ { "id": 2, "text": "abc" } ] } }

to

{
    "id": 1,
    "bar": [
        {
            "id": 2,
            "text": "abc"
        }
    ]
}

Actual behavior

We get an exception:

Got AttributeError when attempting to get a value for field `related` on serializer `PatchDetailSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `Patch` instance.
Original exception text was: 'NoneType' object has no attribute 'patches'.

As discussed in #5489, you need to have a default value if you wish to use dotted notation with a nullable value.

class FooSerializer(serializers.ModelSerializer): bar = _FooSerializer(source='bar.foos', default=None)

class Meta:
    fields = ('id', 'bar')

This fixes things for empty case. However, we also need to specify many=True to correct the not-empty case otherwise we get the following error:

'RelatedManager' object has no attribute 'pk'

If we do that, we now get:

'NoneType' object has no attribute 'patches'

for the empty case 😞