在 Django REST 框架中向序列化器类传递额外的参数

我想把一些参数从 Viewset 传递给 DRF Serializer 类,因为我已经尝试过了:

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

观点

class OneZeroViewSet(viewsets.ModelViewSet):


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


queryset = OneZero.objects.all()

基本上,我想传递一些基于 querystring 的值从视图到 Serializer 类,然后这些将被分配到字段。

事实上,Model 中并不包含这些字段,而是动态创建的字段。

同样的情况在这个问题 堆栈溢出,但我不能理解的答案。

有没有人能在这件事上帮帮我,或者给我一些更好的建议。

94854 次浏览

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.

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

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}

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"]
...

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

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.

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