8

In my User profile model I've included a show_email field explicitly. So, to add this feature to my API, the UserSerializer class looks like this:

class UserSerializer(serializers.ModelSerializer):
    email = serializers.SerializerMethodField('show_email')

    def show_email(self, user):
        return user.email if user.show_email else None

    class Meta:
        model = django.contrib.auth.get_user_model()
        fields = ("username", "first_name", "last_name", "email")

But I don't really like it. I think it would be a lot cleaner if the field email would be completely excluded from the serializer output it show_email is False, instead showing that ugly "email": null thing.

How could I do that?

Grijesh Chauhan
  • 57,103
  • 20
  • 141
  • 208
César García Tapia
  • 3,326
  • 4
  • 27
  • 51

4 Answers4

3

You could do this in your API view by overriding the method returning the response, i.e. the "verb" of the API view. For example, in a ListAPIView you would override get():

class UserList(generics.ListAPIView):
    model = django.contrib.auth.get_user_model()
    serializer_class = UserSerializer

    def get(self, request, *args, **kwargs):
        response = super(UserList, self).get(request, *args, **kwargs)
        for result in response.data['results']:
            if result['email'] is None:
                result.pop('email')
        return response

You would probably want to add some more checking for attributes, but that's the gist of how it could be done. Also, I would add that removing fields from some results may cause issues for the consuming application if it expects them to be present for all records.

Fiver
  • 9,909
  • 9
  • 43
  • 63
3

This answer comes late but for future google searches: there is an example in the documentation about Dynamically modifying fields. So, by passing an argument to the serializer, you control whether or not a field is processed:

serializer = MyPostSerializer(posts, show_email=permissions)

and then in the init function in the serializer you can do something like:

class MyPostSerializer(serializers.ModelSerializer):

def __init__(self, *args, **kwargs):
    show_email = kwargs.pop('show_email', None)

    # Instantiate the superclass normally
    super(DeviceSerializer, self).__init__(*args, **kwargs)

    if not show_email:
        self.fields.pop("show_email")

Now the show_email field will be ignored by the serializer.

StackedUser
  • 327
  • 2
  • 13
  • Give it a Try, it doesn't works. That works for instance's fields (model fields ) `enail` is an extra field defined, You need to delete it for m `._data` to that is not a valid choice (as its name suggests). – Grijesh Chauhan Apr 23 '15 at 19:23
0

You could override restore_fields method on serializer. Here in restore_fields method you can modify list of fields - serializer.fields - pop, push or modify any of the fields.

eg: Field workspace is read_only when action is not 'create'

class MyPostSerializer(ModelSerializer):

def restore_fields(self, data, files):
    if (self.context.get('view').action != 'create'):
        self.fields.get('workspace').read_only=True
    return super(MyPostSerializer, self).restore_fields(data, files)

class Meta:
    model = MyPost
    fields = ('id', 'name', 'workspace')
Jan Míšek
  • 1,647
  • 1
  • 16
  • 22
  • This seems to be OBSOLETE answer. In version 3.2.1 at least there is no method `restore_fields`. Probbably now better way is to use seperate serializers applied in get_serializer of view/set. And for this seperate serializers use inheritance and in Meta modify via extra_kwargs if something is e.g required. – andilabs Apr 14 '17 at 14:43
0

This might be of help... To dynamically include or exclude a field in an API request, the modification of @stackedUser's response below should do:

  class AirtimePurchaseSerializer(serializers.Serializer):

      def __init__(self, *args, **kwargs):
        try:
            phone = kwargs['data']['res_phone_number']
        except KeyError:
            phone = None

        # Instantiate the superclass normally
        super(AirtimePurchaseSerializer, self).__init__(*args, **kwargs)

        if not phone:
            self.fields.pop("res_phone_number")

     res_phone_number = serializers.CharField(max_length=16, allow_null=False)
Atm4God
  • 121
  • 1
  • 4