1

I'm trying to create a writeable nested serialiser for my API endpoint, however upon entering my parent serialiser's create method the nested data doesn't appear in my validated_data dictionary like it is supposed to in the example here. Instead, the nested key is not even present in the dictionary. Instead, it looks like: {'foo': 'bar'}. So, the nested keys appear to be flattened, and any other nested object with the same keys are overwritten.

Any clues as to what the problem might be? I have some fairly complicated validation logic, however after trimming all of this out the problem wasn't recitified, so it appears to be irrelevant.

My models are defined thus:

class Payment(models.Model):
    id = models.AutoField(primary_key=True)
    foo = models.CharField(max_length=15, blank=True, null=True)

class Booking(models.Model):
    id = models.AutoField(primary_key=True)
    payment = models.ForeignKey(Payment, blank=True, null=True)

My serializers:

class PaymentSerializer(serializers.ModelSerializer):
    class Meta:
        model = Payment
        fields = '__all__'

class BookingSerializer(serializers.ModelSerializer):
    payment = PaymentSerializer(source='*', write_only=True)

    def create(self, validated_data):
        print("Creating booking", validated_data) # Outputs "Creating booking {'foo': 'bar'}"
        payment_data = validated_data.pop('payment') # Obviously errors at this point
        primary_guest = Payment.objects.create(payment_data)
        booking = Booking.objects.create(**validated_data)
        # other creation related code

        return booking

    class Meta:
        fields = '__all__'

My ViewSet:

class PrebookingViewSet(viewsets.ModelViewSet):
    queryset = Booking.objects.all().order_by('id')
    serializer_class = BookingSerializer

My request contains the following POST body:

{
    "payment": {
        "foo": "bar"
    }
}
2
  • What are you expecting here -- many foos under a payment? Commented Sep 5, 2018 at 0:26
  • No, I'm expected the validated_data dictionary to be nested as with the documentation example, linked. Much like the request body. My example is highly simplified. Commented Sep 5, 2018 at 0:39

2 Answers 2

1

The thing is you've defined as source='*',.

From the DRF Serializer Doc

The value source='*' has a special meaning, and is used to indicate that the entire object should be passed through to the field. This can be useful for creating nested representations, or for fields which require access to the complete object in order to determine the output representation

That is, your payload {"payment": {"foo": "bar"}} will goes into PaymentSerializer instead of {"foo": "bar"}


Solution
Simply remove the source='*' ;)

class BookingSerializer(serializers.ModelSerializer):
    payment = PaymentSerializer(write_only=True)

    def create(self, validated_data):
        # your code
Sign up to request clarification or add additional context in comments.

2 Comments

That's right, thanks, I just solved it myself. Any hints as to why it had that effect on validated_data in the top-level serialiser? It doesn't seem intuitive.
I think the link I provided is self-explanatory, isn't it?
0

It appears after all the troubleshooting I've done, I stumbled on the solution to the problem more or less while coming up with an example to share in my question. It's a good exercise. The problem was caused by my nested serialiser field definition.

Instead of:

payment = PaymentSerializer(source='*', write_only=True)

I needed:

payment = PaymentSerializer(write_only=True)

My validated_data dictionary now contains a nested dictionary under the payment key.

I'm not sure why I'd included that, I think I've used it elsewhere to unflatten fields into a nested representation. I can't seem to find any documentation on how it's suppoed to work though, and why it caused the behaviour I've seen here. Any input on this is welcome!

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.