DRF says "This field is required." despite everything (?) being defined as optional

Hey all, I got a proper Mystery Error that has me completely stumped.

I am running a Django 4.2.6 + DRF 3.14 backend and there are a few fields that just behave in a way that I believe are explicitly in direct conflict with how I defined them.

I am simplifying this here for better understanding, in reality this is about a whole range of endpoints, but they all derive from common serializers and model field definitions:

Problem: Frontend is making a POST /apiv1/functions/ request with body { "name": "Some Function" }. They are getting a HTTP 400 “Bad Request” response saying { "owner": [ "This field is required." ], "contacts": [ "This field is required." ] }.

This endpoint is handled by a view derived from rest_frameworks.generics.ListCreateAPIView (no custom logic) and the following serializer:

class FunctionSerializer(NodeSerializer):
    contacts = serializers.PrimaryKeyRelatedField(
        queryset=models.Contact.objects.all(), allow_null=True
    )

    class Meta(NodeSerializer.Meta):
        model = models.Function
        fields = '__all__'

which is derived from

class NodeSerializer(serializers.ModelSerializer):
    children = serializers.SerializerMethodField()
    parents = serializers.SerializerMethodField()
    owner = serializers.PrimaryKeyRelatedField(
        queryset=models.Contact.objects.all(), allow_null=True
    )

    created_by = serializers.SlugRelatedField(slug_field='username', read_only=True)
    updated_by = serializers.SlugRelatedField(slug_field='username', read_only=True)

    class Meta:
        model = models.DependencyModelNode
        exclude = (
            'obj_is_deleted',
            'obj_tenant',
            'obj_access_scope',
            'obj_owner',
        )

The model models.Function in question is:

class Function(DependencyModelNode):
    ...
    contacts = models.ForeignKey(
        Contact,
        on_delete=models.RESTRICT,
        related_name='related_functions',
        null=True,
        blank=True,
    )
    ....

with no custom save logic, based on a DependencyModelNode multi-table inheritance parent model:

class DependencyModelNode(models.Model):
    name = models.CharField(max_length=100, blank=False)
    description = models.TextField(null=True, blank=True)
    node_type = models.CharField(max_length=100, null=True, blank=True)
    node_active = models.BooleanField(default=False, blank=True)
    owner = models.ForeignKey(
        to='Contact',
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='%(app_label)s_%(class)s_owned',
    )
    ...

As you can see, the owner and contacts fields are defined in two different places, but are defined to allow null and blank, and the serializers involve explicitly set allow_null=True as well.

I am at my wit’s end with this - why are these fields erroring as “required” when they clearly (?) should not be?

Thanks in advance for any pointers!

That’s a bit tricky in the first place.

But all fields are required by default, and by required this means that the value must be present on the request body.
In other words, you need to send {"owner": null}" in this case (because you have allow_null this is accepted), or set required=False to omit this field.

1 Like

OMG, required=False needs to go on the Serializer? Why do the DRF docs for PrimaryKeyRelatedField not mention that??

I literally spent like 4 hours on this :man_facepalming:

Thank you Leandro!

It’s actually there, but it’s on the core arguments section of the documentation, it applies for every single field.

1 Like

Yikes, ok.

That said, that section also literally says “If you’re using Model Serializer default value will be False if you have specified blank=True or default or null=True at your field in your Model”, which, as you can see, all applies to my models (blank=True AND null=True). So I guess I’ll add required=False everywhere but I kind of thought my model-level definitions already implicitly defined required=False to be triggered.