Django-rest-framework: Nested serializer field required when required = False

Created on 19 Mar 2015  路  19Comments  路  Source: encode/django-rest-framework

Hi there,

I'm on DRF 3.1.0 and Django 1.7.6 and have serializers like below:

class AddressSerializer(serializers.ModelSerializer):
    zip = serializers.RegexField(regex=r'^\d{5}(-\d{4})?$')
    addr1 = serializers.Field()

    class Meta:
        model = Address

class CustomerSerializer(serializers.ModelSerializer):
    ship_address = AddressSerializer(required=False)
    email = serializers.Field()

    class Meta:
        model = Customer

and it works fine when doing like this (usual dict):

CustomerSerializer(data={'email':'[email protected]'}).is_valid() # Gives True, which is ok

BUT it fails when serializer created with request.data in the view, because request.data has MergeDict type

from rest_framework.requests import MergeDict
cs=CustomerSerializer(data=MergeDict({'email':'[email protected]'})) 
cs.is_valid()# Gives False
cs.errors # {'ship_address': {'zip': [u'This field is required.'], 'addr1': [u'This field is required.']}}

I understand that 'zip' and 'addr1' required fields on Address, but in this case Address is not required at all.

Am I doing smth wrong or any ideas how to fix that ?

Thanks in advance

Needs further review

Most helpful comment

I m having exactly the same problem. As mentioned by Anton,

class AddressSerializer(serializers.ModelSerializer):
    class Meta:
        model = Address
        fields = ['house_number', 'street', 'area', 'locality', 'address_of']

class CustomerSerializer(serializers.ModelSerializer):
    ship_address = AddressSerializer(required=False)

    class Meta:
        model = Customer

In Address model Street is a required field.

I want to have the ship_address to be optional in CustomerSerializer.
Inspite of having the required=False flag, I m getting a validation error :

"ship_address": {
        "street": [
            "This field may not be blank."
        ]
    }

Please suggest...

All 19 comments

Please send usage questions to the discussion group, IRC or stack overflow.
The issue tracker is for managing bugs, feature requests and other active work on the project.

Thanks! :)

@tomchristie This looks like a bug,
I also found a person with same problem here https://groups.google.com/forum/?fromgroups#!topic/django-rest-framework/ttq8PUAU424

Having same problem in viewset

class CommentSerializer(serializers.ModelSerializer):
    created_by = UserSerializer(required=False)
    content = serializers.PrimaryKeyRelatedField(queryset=Content.objects.all(), required=False)

    class Meta:
        model = Comment

keeps saying the fields in created_by are required.

Hi @tomchristie, just wondering if this will be fixed anytime soon?

@variable if you need to speed up the process the best way is to help us with a pull request.

Unclear to me if this is a duplicate of #2919.

Hi @variable

keeps saying the fields in created_by are required.

Could you expand on that? What's the error message, and what code is being run that causes it?

Probably not a duplicate. First step is to write the _most minimal possible_ failing test case.

@tomchristie I am not able to reproduce in test case:

class CommentTestCase(TestCase):

    def test_comment(self):
        self.assertEqual(Comment.objects.count(), 0)
        user = User.objects.create_user('testuser', '[email protected]', 'password')
        post = Post.objects.create(created_by=user)
        factory = APIRequestFactory()
        req = factory.post('/api/comments/comments/?content_uuid={}'.format(post.uuid), data={
            'message': 'abc'
        }, format='json')
        force_authenticate(req, user=user)
        CommentViewSet.as_view({'post': 'create'})(req)
        self.assertEqual(Comment.objects.count(), 1)

    def test_comment_1(self):
        self.assertEqual(Comment.objects.count(), 0)
        user = User.objects.create_user('testuser', '[email protected]', 'password')
        post = Post.objects.create(created_by=user)
        client = APIClient()
        client.login(username='testuser', password='password')
        result = client.post('/api/comments/comments/?content_uuid={}'.format(post.uuid), data={
            'message': 'abc'
        }, format='json')
        self.assertEqual(Comment.objects.count(), 1)

The only difference is I am posting via Ajax post, does it make any difference?

    $.post('/api/comments/comments/?content_uuid='+uuid, {message: msg}, function(data){}, 'json');

@tomchristie
I have added "read_only=True" to the created_by field now it works in ajax post, but it seems the read_only flag is trivial since "required=False"

Given this "I am not able to reproduce in test case:" I'm going to assume this was a duplicate of #2919 (just closed now), and close this. More than happy to reconsider if anyone can expand on this with a failing example case.

I m having exactly the same problem. As mentioned by Anton,

class AddressSerializer(serializers.ModelSerializer):
    class Meta:
        model = Address
        fields = ['house_number', 'street', 'area', 'locality', 'address_of']

class CustomerSerializer(serializers.ModelSerializer):
    ship_address = AddressSerializer(required=False)

    class Meta:
        model = Customer

In Address model Street is a required field.

I want to have the ship_address to be optional in CustomerSerializer.
Inspite of having the required=False flag, I m getting a validation error :

"ship_address": {
        "street": [
            "This field may not be blank."
        ]
    }

Please suggest...

The discussion group is the best place to take this point and other usage questions. Thanks!

Looks like this issue was never fixed. Having the same problem as @Anton-Shutik and @sandeepginside and it is not a duplicate of #2919
@xordoquy @tomchristie Can this issue be reopened?

Hi @mihailb. The easiest place to start would be to turn https://github.com/encode/django-rest-framework/issues/2719#issuecomment-393644095 into a failing test case/PR.

Why is this closed? Looks like an important bug to me and can't see any info on that elsewhere. Is there a work around?

By using a custom name instead of the model field name and specifying the source parameter to the serializer, the problem goes away. ie:

user_details = UserSerializer(
    read_only=True,
    source='user',
)

instead of

user = UserSerializer(
    read_only=True,
)

which generates a HTTP 400 field required. Note that this prevents the use of the depth attribute in the serializer's Meta class.

Also, @tomchristie refers this issue in a reply in the mailing list, however no informations help to overcome the problem here. (https://groups.google.com/forum/#!topic/django-rest-framework/ttq8PUAU424)

Reopening, since @sandeepginside's example should be enough to create a test case.

Just a hint for some of you experiencing this - I found that in my case it was caused by a unique_together constrain on the model which forced the field at hand to behave as required=True.
This case is documented in the docs here: https://www.django-rest-framework.org/api-guide/validators/#optional-fields

May be this answer solves your problem:
allow_null

Was this page helpful?
0 / 5 - 0 ratings