Reputation: 135
I override my FormSerializer's update method. It marks some nested field records as is_deprecated if necessary. However, the Api call returns all records, even though my get_queryset filters out everything is_deprecated. As far as I can tell, the get_queryset is being called to get the instance, which is then passed to the serializer. This instance does not contain any previously deprecated records.
After the update method completes, it looks like another query must be run that gets ALL of these nested records and serializes them. This only occurs on updates. This may be occurring when serializer is saved in UpdateModelMixin.update() when self.perform_update() is called.
I get the correct records on a standard GET request. Any idea where this second query is being called and how to override it? Alternatively, I should be able to edit the serialized data in to_representation, but this would be inefficient and I'd like to understand what is going on here first.
View
class FormViewSet(LoginRequiredMixin, viewsets.ModelViewSet):
serializer_class = FormSerializer
queryset = Form.objects.all()
def get_queryset(self):
if 'pk' in self.kwargs:
qs = Form.objects.filter(id=self.kwargs['pk'])
else:
qs = Form.objects.filter(id__in=name_dict.values()).order_by('name')
queryset = FormSerializer.eager_loading(qs)
return queryset
Serializer
class FormSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(required=False, allow_null=True)
fields = FieldSerializer(many=True)
class Meta:
model = Form
fields = '__all__'
@staticmethod
def eager_loading(queryset):
return queryset.prefetch_related(Prefetch('fields',queryset=Field.objects.filter(is_deprecated=False).order_by('field_order')))
Upvotes: 3
Views: 808
Reputation: 6296
If you look at the update method for the modelviewset, you will see that it invalidates the prefetch_related
data so your eager loading doesn't work.
def update(self, request, *args, **kwargs):
partial = kwargs.pop('partial', False)
instance = self.get_object()
serializer = self.get_serializer(instance, data=request.data, partial=partial)
serializer.is_valid(raise_exception=True)
self.perform_update(serializer)
if getattr(instance, '_prefetched_objects_cache', None):
# If 'prefetch_related' has been applied to a queryset, we need to
# forcibly invalidate the prefetch cache on the instance.
instance._prefetched_objects_cache = {}
return Response(serializer.data)
This is done because the prefetched data could become obsolete after the update. So you can override the update method and redo the prefetch after the update
Upvotes: 1
Reputation: 101
I think, this answer should be a comment, but have no enough reputation.
get_queryset
method called before update
method of serializer.
What I mean by that:
update
-> PUT for viewget_queryset
-> Now we have not updated is_deprecated
yetupdate
of serializer -> You make changesupdate
method of serializer So your fields
field is same as on 2 point. Doesn't eager_loading, prefetch works like that? I mean loaded once, and that's it.
So you need to somehow manage to update your prefetched fields
field.
Upvotes: 1