Skip to content

Commit

Permalink
Fix UpdateModelMixin to work when no queryset is defined
Browse files Browse the repository at this point in the history
Fix #9306
  • Loading branch information
browniebroke committed Mar 19, 2024
1 parent 77ef27f commit 80e09d0
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 6 deletions.
13 changes: 7 additions & 6 deletions rest_framework/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
We don't bind behaviour to http method handlers yet,
which allows mixin classes to be composed in interesting ways.
"""
from django.db.models.query import prefetch_related_objects
from django.db.models.query import prefetch_related_objects, QuerySet

from rest_framework import status
from rest_framework.response import Response
Expand Down Expand Up @@ -69,13 +69,14 @@ def update(self, request, *args, **kwargs):
serializer.is_valid(raise_exception=True)
self.perform_update(serializer)

queryset = self.filter_queryset(self.get_queryset())
if queryset._prefetch_related_lookups:
if hasattr(instance, '_prefetched_objects_cache'):
# If 'prefetch_related' has been applied to a queryset, we need to
# forcibly invalidate the prefetch cache on the instance,
# and then re-prefetch related objects
# forcibly invalidate the prefetch cache on the instance
instance._prefetched_objects_cache = {}
prefetch_related_objects([instance], *queryset._prefetch_related_lookups)
queryset = self.filter_queryset(self.get_queryset())
if getattr(queryset, '_prefetch_related_lookups', None):
# And then re-prefetch related objects
prefetch_related_objects([instance], *queryset._prefetch_related_lookups)

return Response(serializer.data)

Expand Down
15 changes: 15 additions & 0 deletions tests/test_prefetch_related.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ class UserUpdateWithoutPrefetchRelated(generics.UpdateAPIView):
serializer_class = UserSerializer


class UserRetrieveWithoutQuerySet(generics.RetrieveUpdateAPIView):
serializer_class = UserSerializer

def get_object(self):
return User.objects.get(pk=self.kwargs['pk'])


class TestPrefetchRelatedUpdates(TestCase):
def setUp(self):
self.user = User.objects.create(username='tom', email='tom@example.com')
Expand Down Expand Up @@ -90,3 +97,11 @@ def test_db_query_count(self):
)
with self.assertNumQueries(16):
UserUpdateWithoutPrefetchRelated.as_view()(request, pk=self.user.pk)

def test_can_update_without_queryset(self):
request = factory.patch('/', {'username': 'new'})
response = UserRetrieveWithoutQuerySet.as_view()(request, pk=self.user.pk)
assert response.data['id'] == self.user.id
assert response.data['username'] == 'new'
self.user.refresh_from_db()
assert self.user.username == 'new'

0 comments on commit 80e09d0

Please sign in to comment.