This is a toy example of the problem that I am facing. For some reason, I am unable to pass the expected data to my serializer and that is raising the following error.
AttributeError at /my-end-point/
Got AttributeError when attempting to get a value for field main_data
on serializer ParentSerializer
. The serializer field might be named incorrectly and not match any attribute or key on the str
instance. Original exception text was: 'str' object has no attribute 'main_data'.
class MainModelSerializer(serializers.ModelSerializer):
class Meta:
model = MainModel
class ParentSerializer(serializers.Serializer):
main_data = MainModelSerializer(many=True)
extra_data = serializers.FloatField()
class View(ListCreateAPIView):
serializer = ParentSerializer
def get_queryset(self):
# extra_data would have some calculated value which would be a float
extra_data = some_calculated_value()
queryset = MainModel.objects.filter(some filters)
return {
'main_data': queryset,
'extra_data': extra_data
}
# expected data that is passed to the ParentSerializer
# {
# 'main_data': queryset,
# 'extra_data': extra_data
# }
Look's like you have two problems:
get_queryset
method As for the first one you simply should not override that method or return a queryset. Depending on how extra data is being computing there can be the tradeoffs like annotating value on queryset and specifying it in serializer fields list .
For the second one - if annotation is not an option and you'd like to calculate value one's, the possible solution is to add extra data to serializer context and use it as return value of serializer method field . In this case extra data will be placed on the same level with model records data (extra field for each model record):
{
'model_field_1': 'value',
...
'extra_field': 'value',
}
Or you can continue using your approach with nested relationships by override list
(and create
, if needed) - just add extra data to serializer validated data, so that the result will look like this:
{
'extra_data': 'value',
'main_data': [
{'id': 1, 'field1': 'value', ...}
...
]
}
class View(ListCreateAPIView):
serializer = ParentSerializer
def list(self, request, *args, **kwargs):
# extra_data would have some calculated value which would be a float
extra_data = some_calculated_value()
qs = self.get_queryset()
data = self.get_serializer(qs, many=True).data
data['extra_data'] = extra_data
return Response({'data': data}, status=status.HTTP_200_OK, content_type = 'application/json' )
I think what you want to achieve is to pass some extra data to the ParentSerializer
to be returned by the list action
on your view.But you have multiple problems in your code:
get_queryset
is only used to return an instance of models.QuerySet
, which is used to determine which model objects the view is allowed to perform any kind of actions on. get_queryset
is not used to pass data to the serializer, this is done during the Serializer
initialization.ModelSerializer
but you're not specifying the fields
attribute on the Meta
class which is not allowed anymore since drf version 3.3
, you have to define either a fields
attribute or an exclude
attribute.As for actually how to pass that extra data to the Serializer, you can do that in multiple ways:
class ParentSerializer(serializers.Serializer):
main_data = MainDataSerializer(many=True)
extra_data = serializers.SerializerMethodField(method_name="some_calculated_value")
def some_calculated_value(self, obj):
# calculate value here
return value
Note that this field is a read only field.
class ParentSerializer(serializers.Serializer):
main_data = MainDataSerializer(many=True)
def validate(self, attrs):
attrs["extra_data"] = some_calculated_value()
return attrs
list
function on the View and change the data passed to the serializer. I personally prefer option number 1 since SerializerMethodField
fits the purpose, while the validate
function should be used only for validation to abide by the single responsibility concept.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.