Dynamically include or exclude Serializer class fi

2019-03-20 23:16发布

问题:

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?

回答1:

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.



回答2:

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.



回答3:

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')