Skip to content

Commit da78a14

Browse files
authored
Revert "Re-prefetch related objects after updating (#8043)" (#9327)
This reverts commit 2b34aa4.
1 parent 0e4ed81 commit da78a14

File tree

2 files changed

+32
-71
lines changed

2 files changed

+32
-71
lines changed

rest_framework/mixins.py

+2-7
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
We don't bind behaviour to http method handlers yet,
55
which allows mixin classes to be composed in interesting ways.
66
"""
7-
from django.db.models.query import prefetch_related_objects
8-
97
from rest_framework import status
108
from rest_framework.response import Response
119
from rest_framework.settings import api_settings
@@ -69,13 +67,10 @@ def update(self, request, *args, **kwargs):
6967
serializer.is_valid(raise_exception=True)
7068
self.perform_update(serializer)
7169

72-
queryset = self.filter_queryset(self.get_queryset())
73-
if queryset._prefetch_related_lookups:
70+
if getattr(instance, '_prefetched_objects_cache', None):
7471
# If 'prefetch_related' has been applied to a queryset, we need to
75-
# forcibly invalidate the prefetch cache on the instance,
76-
# and then re-prefetch related objects
72+
# forcibly invalidate the prefetch cache on the instance.
7773
instance._prefetched_objects_cache = {}
78-
prefetch_related_objects([instance], *queryset._prefetch_related_lookups)
7974

8075
return Response(serializer.data)
8176

tests/test_prefetch_related.py

+30-64
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
from django.contrib.auth.models import Group, User
2-
from django.db.models.query import Prefetch
32
from django.test import TestCase
43

54
from rest_framework import generics, serializers
@@ -9,84 +8,51 @@
98

109

1110
class UserSerializer(serializers.ModelSerializer):
12-
permissions = serializers.SerializerMethodField()
13-
14-
def get_permissions(self, obj):
15-
ret = []
16-
for g in obj.groups.all():
17-
ret.extend([p.pk for p in g.permissions.all()])
18-
return ret
19-
2011
class Meta:
2112
model = User
22-
fields = ('id', 'username', 'email', 'groups', 'permissions')
23-
24-
25-
class UserRetrieveUpdate(generics.RetrieveUpdateAPIView):
26-
queryset = User.objects.exclude(username='exclude').prefetch_related(
27-
Prefetch('groups', queryset=Group.objects.exclude(name='exclude')),
28-
'groups__permissions',
29-
)
30-
serializer_class = UserSerializer
13+
fields = ('id', 'username', 'email', 'groups')
3114

3215

33-
class UserUpdateWithoutPrefetchRelated(generics.UpdateAPIView):
34-
queryset = User.objects.exclude(username='exclude')
16+
class UserUpdate(generics.UpdateAPIView):
17+
queryset = User.objects.exclude(username='exclude').prefetch_related('groups')
3518
serializer_class = UserSerializer
3619

3720

3821
class TestPrefetchRelatedUpdates(TestCase):
3922
def setUp(self):
4023
self.user = User.objects.create(username='tom', email='[email protected]')
41-
self.groups = [Group.objects.create(name=f'group {i}') for i in range(10)]
24+
self.groups = [Group.objects.create(name='a'), Group.objects.create(name='b')]
4225
self.user.groups.set(self.groups)
43-
self.user.groups.add(Group.objects.create(name='exclude'))
44-
self.expected = {
45-
'id': self.user.pk,
46-
'username': 'tom',
47-
'groups': [group.pk for group in self.groups],
48-
'email': '[email protected]',
49-
'permissions': [],
50-
}
51-
self.view = UserRetrieveUpdate.as_view()
5226

5327
def test_prefetch_related_updates(self):
54-
self.groups.append(Group.objects.create(name='c'))
55-
request = factory.put(
56-
'/', {'username': 'new', 'groups': [group.pk for group in self.groups]}, format='json'
57-
)
58-
self.expected['username'] = 'new'
59-
self.expected['groups'] = [group.pk for group in self.groups]
60-
response = self.view(request, pk=self.user.pk)
61-
assert User.objects.get(pk=self.user.pk).groups.count() == 12
62-
assert response.data == self.expected
63-
# Update and fetch should get same result
64-
request = factory.get('/')
65-
response = self.view(request, pk=self.user.pk)
66-
assert response.data == self.expected
28+
view = UserUpdate.as_view()
29+
pk = self.user.pk
30+
groups_pk = self.groups[0].pk
31+
request = factory.put('/', {'username': 'new', 'groups': [groups_pk]}, format='json')
32+
response = view(request, pk=pk)
33+
assert User.objects.get(pk=pk).groups.count() == 1
34+
expected = {
35+
'id': pk,
36+
'username': 'new',
37+
'groups': [1],
38+
'email': '[email protected]'
39+
}
40+
assert response.data == expected
6741

6842
def test_prefetch_related_excluding_instance_from_original_queryset(self):
6943
"""
7044
Regression test for https://github.com/encode/django-rest-framework/issues/4661
7145
"""
72-
request = factory.put(
73-
'/', {'username': 'exclude', 'groups': [self.groups[0].pk]}, format='json'
74-
)
75-
response = self.view(request, pk=self.user.pk)
76-
assert User.objects.get(pk=self.user.pk).groups.count() == 2
77-
self.expected['username'] = 'exclude'
78-
self.expected['groups'] = [self.groups[0].pk]
79-
assert response.data == self.expected
80-
81-
def test_db_query_count(self):
82-
request = factory.put(
83-
'/', {'username': 'new'}, format='json'
84-
)
85-
with self.assertNumQueries(7):
86-
self.view(request, pk=self.user.pk)
87-
88-
request = factory.put(
89-
'/', {'username': 'new2'}, format='json'
90-
)
91-
with self.assertNumQueries(16):
92-
UserUpdateWithoutPrefetchRelated.as_view()(request, pk=self.user.pk)
46+
view = UserUpdate.as_view()
47+
pk = self.user.pk
48+
groups_pk = self.groups[0].pk
49+
request = factory.put('/', {'username': 'exclude', 'groups': [groups_pk]}, format='json')
50+
response = view(request, pk=pk)
51+
assert User.objects.get(pk=pk).groups.count() == 1
52+
expected = {
53+
'id': pk,
54+
'username': 'exclude',
55+
'groups': [1],
56+
'email': '[email protected]'
57+
}
58+
assert response.data == expected

0 commit comments

Comments
 (0)