Django-rest-framework: Serializer .to_representation() doesn't call field.to_representation() for attributes of value None

Created on 17 Dec 2014  路  13Comments  路  Source: encode/django-rest-framework

Not sure if this is a bug/feature/other but I have a situation with an "is_deleted" field in my serializer that takes a source deleted_datetime field from the model. The field is set to NULL/None when the item is not deleted, and a datetime when deleted. The API shows is_deleted as True (datetime set) or False (when NULL).

However, the Serializer .to_representation only calls the field .to_representation when the attribute is not None. This means is_deleted comes back as either True or None.

I've had to override the get_attribute method in the field to call the field's parent get_attribute and then map accordingly, but this doesn't feel right as I'm doing the same work as the .to_representation method.

Am I missing the obvious? It's been a long week already! :-)

Needs design decision

All 13 comments

Normally we want to skip to_representation for the None case. Failing to do so means we end up having to deal with None as a special case in every implementation of to_representation, which users will invariable forget to deal with.

Any thoughts on what would be an appropriate resolution to this from your point of view?

I think this one sounds like a good usecase for SerializerMethodField()

is_deleted = SerializerMethodField()

def get_is_deleted(value):
    return False if (value is None) else True

Sound like a decent approach?

Aha! Thanks - that definitely looks like a better approach!

1,000th closed issue :tada:

Cool :-)

Yup. 1001 now. And a further 1232 pull requests.

Over slightly less that 4 years, thats 2233 / (365 * 4) ---> More than 1.5 closes per day, every day of the week for a solid 4 years.

That's a whole lotta work.

Want to take the chance on this famous issue to thank @tomchristie and all the maintainers and contributors for all their hard work for the past 4 years. Thank you for this awesome framework :)

I would like to reopen this issue since I think that it would be a nice feature to have and right now I'm migrating an old codebase to DRF and I have a field were None needs more parsing.

I think that a good solution is to have an attribute on Field that defaults to False and you can override it to force the call of to_representation

If you like the idea I'll start working on a PR

@GonzaloRizzo I don't think it's up to the serializer to do that work. I believe that's part of the business / service layer (could be merged within the model layer) to provide the informations.

What about BooleanField -- as opposed to NullBooleanField it kind of explicitly states that does not leave null/None, but will give it in serialization:

class S(Serializer):
    bf = BooleanField(required=False, read_only=True)

class example:
    bf = None  # I expect it to become **False**

s = S(instance=example)  
assert s.data['bf'] == False # Fails, because {'bf': None} 

For me it looks like a bug..

Don鈥檛 do that.

not intentionally, but subquery'ing, sth like

Person.objects.annotate(is_occupied=Subquery(
        Event.objects.
            .filter(person=OuterRef('pk'))
            .values('occupied')[:1],
        output_field=BooleanField()
    )
)

when none events found, gives None...

I can/will cast now, but docs could mention this behaviour (at least in "strict" BooleanField topic)
Thanks

I also think this is unexpected behavior to skip to_representation for None values. None is also value like any other value, and skipping it breaks the idea of customized field. For example I have

class UserSerializer(serializers.ModelSerializer):
    country = CustomCountryField(Country, CountrySerializer)

and I want to use this serializer for read and write, so SerializerModelField does not fit this case as it does not support set_field_name. Besides, the main reason to customize this field in my case is to set fallback value {} on read if country is None.

At least this "optimization" can be made configurable like skip_empty=False.
I can implement this improvement if you guys agree with this idea

Was this page helpful?
0 / 5 - 0 ratings