Search code examples
djangodjango-rest-frameworkdjango-rest-viewsets

Break up field into its own serializer in Django Rest Framework


Let's say I have simple Product Django model:

class Product:
    name = models.CharField(max_length=255, unique=True)
    created_on = models.DateField()

I'm using Django Rest Framework to serialize this model. I'd like to break up created_on into its own object (including the response from GET requests and the payload in POST requests):

{
    "name": "MyProduct",
    "created_on": {
        "year": 2020,
        "month": 1,
        "day": 24
    }
}

Here's what I have so far:

class DateSerializer(serializers.Serializer):
    year = serializers.IntegerField()
    month = serializers.IntegerField()
    day = serializers.IntegerField()

    def validate(data):
        return datetime.date(data["year"], data["month"], data["day"])

class ProductSerializer(serialzers.ModelSerializer):
    created_on = DateSerializer()

    class Meta:
        model = Friend
        fields = ("name", "created_on")

    def create(self, validated_data):
        return Product.objects.create(**validated_data)

class ProductViewset(viewsets.ModelViewSet):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer

This approach works for the GET requests (that is, I get the json above). However, it doesn't work for POST requests (payload being the json above). The response is 400 status code with the message {'created_on': [ErrorDetail(string='This field is required.', code='required')]}.

If I pass required=False into DateSerializer, I see self.initial_data in the create method is <QueryDict: {'name': ['MyProduct'], 'created_on': ['year', 'month', 'day']}>. So the values disappear for some reason.

Any idea what I'm doing wrong here and how I can get this to work?


Solution

  • Figured it out, I need to set the content_type=application/json header in the request. Otherwise it defaults to content_type=multipart/form-data which flattens the payload to {'name': ['MyProduct'], 'created_on': ['year', 'month', 'day']}.