Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

django-rest-framework serializer different fields in multiple views

I am new at Django and couldn't find solution for my problem.

The problem is to force specific serializer for include different amount of fields in case of utilizing different views. I would like to use 'id' field in my 1st view, and in 2nd view - 'id' and 'name' fields.

Here is my model.py

class Processing(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField()
    description = models.CharField()

And here is my serializer.py

class ProcessingSerializer(serializers.ModelSerializer):
    id = serializers.ModelField(model_field=Processing()._meta.get_field('id'))
    class Meta:
        model = Processing
        fields = ('id', 'name')

Any help will be welcome.

like image 294
sorryMike Avatar asked May 19 '17 07:05

sorryMike


People also ask

How do you pass extra context data to Serializers in Django REST framework?

In function based views we can pass extra context to serializer with "context" parameter with a dictionary. To access the extra context data inside the serializer we can simply access it with "self. context". From example, to get "exclude_email_list" we just used code 'exclude_email_list = self.

What is nested serializer in Django REST framework?

DRF provides a Serializer class that gives you a powerful, generic way to control the output of your responses, as well as a ModelSerializer class that provides a useful shortcut for creating serializers that deal with model instances and querysets.

What is the difference between ModelSerializer and HyperlinkedModelSerializer?

The HyperlinkedModelSerializer class is similar to the ModelSerializer class except that it uses hyperlinks to represent relationships, rather than primary keys. By default the serializer will include a url field instead of a primary key field.


3 Answers

When someone just starts using DRF, a common mistake is to try to make the same Serializer do everything under the sun. Certainly I went down that path myself.

but life becomes a lot simpler when you use mutiple serializers for different tasks. You can easily switch serializers using the get_serializer_class method. Here is an example right from the manual that shows how to use one for admins and another for ordinary users

def get_serializer_class(self):
    if self.request.user.is_staff:
        return FullAccountSerializer
    return BasicAccountSerializer

Sometimes you want to use a single serializer for lists and another one for when providing details. Try something like this:

def get_serializer_class(self):
    if self.action == 'retrieve':
        return serializers.PlayerDetailSerializer
    else : 
        return serializers.PlayerSerializer

Life is much simpler this way.

like image 170
e4c5 Avatar answered Oct 07 '22 22:10

e4c5


class DynamicFieldsModelSerializer(ModelSerializer):
    """
    A ModelSerializer that takes an additional `fields` and 'exclude' argument that
    controls which fields should be displayed.
    """

    def __init__(self, *args, **kwargs):
        # Don't pass the 'fields' arg up to the superclass
        fields = kwargs.pop('fields', None)
        exclude = kwargs.pop('exclude', None)

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

        if fields is not None:
            # Drop any fields that are not specified in the `fields` argument.
            allowed = set(fields)
            existing = set(self.fields.keys())
            for field_name in existing - allowed:
                self.fields.pop(field_name)

        if exclude is not None:
            not_allowed = set(exclude)
            for exclude_name in not_allowed:
                self.fields.pop(exclude_name)



class UserCreateSerializer(DynamicFieldsModelSerializer):
    class Meta:
        model = User
        fields = ('username', 'tel', 'email', 'password')

use:

serializer = UserCreateSerializer(data=request.data, fields=('username', 'password', 'tel'))

or

serializer = UserCreateSerializer(data=request.data, fields=('username', 'password', 'email'))
like image 38
Ykh Avatar answered Oct 07 '22 20:10

Ykh


You can also use the next approach:

class SelectSerializerMixin(object):
    serializer_class = None
    list_serializer_class = None
    retrieve_serializer_class = None
    update_serializer_class = None
    partial_update_serializer_class = None
    create_serializer_class = None

    def get_serializer_class(self):
        """
        Return the class to use for the serializer.
        Defaults to using `self.serializer_class`.
        """
        assert self.serializer_class is not None, (
            "'%s' should either include a `serializer_class` attribute, "
            "or override the `get_serializer_class()` method."
            % self.__class__.__name__
        )
        return getattr(self, f"{self.action}_serializer_class") or self.serializer_class

Then add this mixin to your ViewSet:

class MyModelViewSet(SelectSerializerMixin, ModelViewSet):
    queryset = models.MyModel.objects.all()
    serializer_class = serializers.SomeSerializer
    retrieve_serializer_class = serializers.AnotherSerializer
    list_serializer_class = serializers.OneMoreSerializer

But if you need a Serializer with a dynamic set of fields (ex. you have a handler but you need to return only specific fields in the response), you can use the approach from Ykh's answer.

https://stackoverflow.com/a/44064046/2818865

like image 43
redcyb Avatar answered Oct 07 '22 20:10

redcyb