2

I have a model with a nullable boolean field that I'd like to have serialized in a way that converts null in the output to false.

My model:

class UserPreferences(models.Model):
    receive_push_notifications = models.BooleanField(
        null=True, blank=True,
        help_text=("Receive push notifications))

I'm trying to do it with a custom field like so:

class StrictlyBooleanField(serializers.Field):
    def to_representation(self, value):
        # Force None to False
        return bool(value)

    def to_internal_value(self, data):
        return bool(data)


class UserPreferencesSerializer(serializers.ModelSerializer):
    class Meta(object):
        model = UserPreferences
        fields = ('receive_push_notifications',)

    receive_push_notifications = StrictlyBooleanField()

but this isn't working, I'm still seeing null in my API responses.

I think I must be missing something simple in wiring it up because I don't even get an error if I replace my to_representation with:

def to_representation(self, value):
    raise

DRF doesn't seem to be calling my method at all... What am I missing here?

mgalgs
  • 15,671
  • 11
  • 61
  • 74
  • When do you need to set `null=False`? On create, update or delete? By the way, the function `to_representation()` I personally use to display the data in a custom way. Like, key as field and field as key... – Elias Prado May 07 '22 at 04:22
  • please check this question and its answers, This will be useful to take you next step to fix. https://stackoverflow.com/questions/14583816/django-rest-framework-how-to-add-custom-field-in-modelserializer – NANDHA KUMAR May 07 '22 at 16:36
  • @EliasPrado it's on get. Just trying to change it in my GET response. – mgalgs May 09 '22 at 20:13

2 Answers2

1

Explanation

After looking into rest framework's Serializer's to_representation method, you will find that it iterates through all of the fields and calls field.get_attribute method for each field. If the value returned from that method is None it skips calling field.to_representation entirely and set None as the field value.

# Serializer's to_representation method
def to_representation(self, instance):
    """
    Object instance -> Dict of primitive datatypes.
    """
    ret = OrderedDict()
    fields = self._readable_fields

    for field in fields:
        try:
            attribute = field.get_attribute(instance)
        except SkipField:
            continue

        # We skip `to_representation` for `None` values so that fields do
        # not have to explicitly deal with that case.
        #
        # For related fields with `use_pk_only_optimization` we need to
        # resolve the pk value.
        check_for_none = attribute.pk if isinstance(attribute, PKOnlyObject) else attribute
        if check_for_none is None:
            ret[field.field_name] = None
        else:
            ret[field.field_name] = field.to_representation(attribute)

    return ret

Solution

Override field.get_attribute by calling super().get_attribute and return False if the value is None

class StrictlyBooleanField(serializers.Field):
    def get_attribute(self, instance):
        attribute = super().get_attribute(instance)
        return bool(attribute)

    def to_representation(self, value):
        return value

    def to_internal_value(self, data):
        return bool(data)
annonymous
  • 741
  • 3
  • 6
0

You can just write a simple function inside your serializer

 class UserPreferencesSerializer(serializers.ModelSerializer):
     yourField = serializers.SerializerMethodField(read_only=True)
     class Meta(object):
         model = UserPreferences
         fields = ['receive_push_notifications', 'yourField']

         def get_yourField(self, obj):
              if obj.receive_push_notifications == null:
                   return False
              else:
                   return True
shaswat kumar
  • 369
  • 8
  • 19
  • Yeah, but I have a dozen or so such nullable boolean fields... That's a lot of boilerplate I'd like to avoid. Seems like the perfect use case for a custom field. – mgalgs May 09 '22 at 16:22