Shoaib Ijaz
Shoaib Ijaz

Reputation: 5587

Pass extra arguments to Serializer Class in Django Rest Framework

I want to pass some arguments to DRF Serializer class from Viewset, so for I have tried this:

class OneZeroSerializer(rest_serializer.ModelSerializer):

    def __init__(self, *args, **kwargs):
        print args # show values that passed

    location = rest_serializer.SerializerMethodField('get_alternate_name')

    def get_alternate_name(self, obj):
        return ''


    class Meta:
        model = OneZero

        fields = ('id', 'location')

Views

class OneZeroViewSet(viewsets.ModelViewSet):

   serializer_class = OneZeroSerializer(realpart=1)
   #serializer_class = OneZeroSerializer

   queryset = OneZero.objects.all()

Basically I want to pass some value based on querystring from views to Serializer class and then these will be allocate to fields.

These fields are not include in Model in fact dynamically created fields.

Same case in this question stackoverflow, but I cannot understand the answer.

Can anyone help me in this case or suggest me better options.

Upvotes: 111

Views: 115068

Answers (8)

Sheheryar Ahmed
Sheheryar Ahmed

Reputation: 1

you can pass the arguments as keyword arguments when you initialize the serializer in your Viewset. For example:

class OneZeroViewSet(viewsets.ModelViewSet):
     serializer_class = OneZeroSerializer

def list(self, request):
    some_argument = 'some value'
    serializer = self.get_serializer(context={'arg_name': some_argument}, many=True)
    return rest_response.Response(serializer.data)

Then, you can access the arg_name value in your serializer's init method by using the context.

class OneZeroSerializer(serializer.ModelSerializer):
     def __init__(self, *args, **kwargs):
         argument = kwargs.pop('context', {}).get('arg_name')
         print(argument)
         super().__init__(*args, **kwargs)

class Meta:
    model = OneZero
    fields = ('id', 'location')

Upvotes: 0

Getting the context kwargs passed to a serializer like;

...
self.fields['category'] = HouseCategorySerializer(read_only=True, context={"all_fields": False})
...

In your serializer, that is HouseCategorySerializer do this in one of your functions

def get_houses(self, instance):
    print(self._context.get('all_fields'))

Using self._context.get('keyword') solved my mess quickly, instead of using self.get_extra_context()

Upvotes: 1

Angelo
Angelo

Reputation: 11

List of element if your query is a list of elements:

my_data = DataSerializers(queryset_to_investigate, 
                          many=True, context={'value_to_pass': value_passed}

in case off single data query:

my_data = DataSerializers(queryset_to_investigate, 
                          context={'value_to_pass': value_passed}

Then in the serializers:

class MySerializer(serializers.ModelSerializer):
    class Meta:
        fields = '__all__'
        model = 'Name_of_your_model'

    def on_representation(self, value):
        serialized_data = super(MySerializer, self).to_representation(value)
        value_as_passed = self.context['value_to_pass']
        # ..... do all you need ......
        return serialized_data

As you can see printing the self inside on_representation you can see: query_set: <object (x)>, context={'value_to_pass': value_passed}

This is a simpler way, and you can do this in any function of serializers having self in the parameter list.

Upvotes: 1

M.Void
M.Void

Reputation: 2894

You could in the YourView override get_serializer_context method like that:

class YourView(GenericAPIView):

    def get_serializer_context(self):
        context = super().get_serializer_context()
        context["customer_id"] = self.kwargs['customer_id']
        context["query_params"] = self.request.query_params
        return context

or like that:

class YourView(GenericAPIView):
    def post(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)

        serializer.context["customer_id"] = request.user.id
        serializer.context["query_params"] = request.query_params

        serializer.is_valid(raise_exception=True)
        ...

and anywhere in your serializer you can get it. For example in a custom method:

class YourSerializer(ModelSerializer):
    def get_alternate_name(self, obj):
        customer_id = self.context["customer_id"]
        query_params = self.context["query_params"]
        ...

Upvotes: 52

spencer.pinegar
spencer.pinegar

Reputation: 460

These answers are far to complicated; If you have any sort of authentication then add this property to your serializer and call it to access the user sending the request.

class BaseSerializer(serializers.ModelSerializer):

@property
def sent_from_user(self):
    return self.context['request'].user

Upvotes: 0

redcyb
redcyb

Reputation: 1697

It's very easy with "context" arg for "ModelSerializer" constructor.

For example:

in view:

my_objects = MyModelSerializer(
    input_collection, 
    many=True, 
    context={'user_id': request.user.id}
).data

in serializers:

class MyModelSerializer(serializers.ModelSerializer):
...

    is_my_object = serializers.SerializerMethodField('_is_my_find')
...

    def _is_my_find(self, obj):
        user_id = self.context.get("user_id")
        if user_id:
            return user_id in obj.my_objects.values_list("user_id", flat=True)
        return False
...

so you can use "self.context" for getting extra params.

Reference

Upvotes: 156

andilabs
andilabs

Reputation: 23281

To fulfill the answer of redcyb - consider using in your view the get_serializer_context method from GenericAPIView, like this:

def get_serializer_context(self):
    return {'user': self.request.user.email}

Upvotes: 29

yeaske
yeaske

Reputation: 1432

A old code I wrote, that might be helpful- done to filter nested serializer:

class MySerializer(serializers.ModelSerializer):

    field3  = serializers.SerializerMethodField('get_filtered_data')

    def get_filtered_data(self, obj):
        param_value = self.context['request'].QUERY_PARAMS.get('Param_name', None)
        if param_value is not None:
            try:
                data = Other_model.objects.get(pk_field=obj, filter_field=param_value)
            except:
                return None
            serializer = OtherSerializer(data)
            return serializer.data
        else:
            print "Error stuff"

    class Meta:
        model = Model_name
        fields = ('filed1', 'field2', 'field3')

How to override get_serializer_class:

class ViewName(generics.ListAPIView):

    def get_serializer_class(self):
        param_value = self.context['request'].QUERY_PARAMS.get('Param_name', None)
        if param_value is not None:
            return Serializer1
        else:
            return Serializer2

    def get_queryset(self):
       .....

Hope this helps people looking for this.

Upvotes: 14

Related Questions