Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Optimizations play nicely with select_related, prefetch_related
  • Loading branch information
lovelydinosaur committed Dec 10, 2014
commit ca7b1f6d5189398be8a0d24b1e01577281b1b187
12 changes: 1 addition & 11 deletions rest_framework/relations.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,17 +101,7 @@ def get_attribute(self, instance):

def get_iterable(self, instance, source_attrs):
relationship = get_attribute(instance, source_attrs)
relationship = relationship.all() if (hasattr(relationship, 'all')) else relationship

if self.use_pk_only_optimization():
# Optimized case, return mock objects only containing the pk attribute.
return [
PKOnlyObject(pk=pk)
for pk in relationship.values_list('pk', flat=True)
]

# Standard case, return the object instances.
return relationship
return relationship.all() if (hasattr(relationship, 'all')) else relationship

@property
def choices(self):
Expand Down
6 changes: 6 additions & 0 deletions tests/test_relations_hyperlink.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,12 @@ def test_many_to_many_retrieve(self):
with self.assertNumQueries(4):
self.assertEqual(serializer.data, expected)

def test_many_to_many_retrieve_prefetch_related(self):
queryset = ManyToManySource.objects.all().prefetch_related('targets')
serializer = ManyToManySourceSerializer(queryset, many=True, context={'request': request})
with self.assertNumQueries(2):
serializer.data

def test_reverse_many_to_many_retrieve(self):
queryset = ManyToManyTarget.objects.all()
serializer = ManyToManyTargetSerializer(queryset, many=True, context={'request': request})
Expand Down
12 changes: 12 additions & 0 deletions tests/test_relations_pk.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,12 @@ def test_many_to_many_retrieve(self):
with self.assertNumQueries(4):
self.assertEqual(serializer.data, expected)

def test_many_to_many_retrieve_prefetch_related(self):
queryset = ManyToManySource.objects.all().prefetch_related('targets')
serializer = ManyToManySourceSerializer(queryset, many=True)
with self.assertNumQueries(2):
serializer.data

def test_reverse_many_to_many_retrieve(self):
queryset = ManyToManyTarget.objects.all()
serializer = ManyToManyTargetSerializer(queryset, many=True)
Expand Down Expand Up @@ -188,6 +194,12 @@ def test_reverse_foreign_key_retrieve(self):
with self.assertNumQueries(3):
self.assertEqual(serializer.data, expected)

def test_reverse_foreign_key_retrieve_prefetch_related(self):
queryset = ForeignKeyTarget.objects.all().prefetch_related('sources')
serializer = ForeignKeyTargetSerializer(queryset, many=True)
with self.assertNumQueries(2):
serializer.data

def test_foreign_key_update(self):
data = {'id': 1, 'name': 'source-1', 'target': 2}
instance = ForeignKeySource.objects.get(pk=1)
Expand Down
15 changes: 14 additions & 1 deletion tests/test_relations_slug.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,14 @@ def test_foreign_key_retrieve(self):
{'id': 2, 'name': 'source-2', 'target': 'target-1'},
{'id': 3, 'name': 'source-3', 'target': 'target-1'}
]
self.assertEqual(serializer.data, expected)
with self.assertNumQueries(4):
self.assertEqual(serializer.data, expected)

def test_foreign_key_retrieve_select_related(self):
queryset = ForeignKeySource.objects.all().select_related('target')
serializer = ForeignKeySourceSerializer(queryset, many=True)
with self.assertNumQueries(1):
serializer.data

def test_reverse_foreign_key_retrieve(self):
queryset = ForeignKeyTarget.objects.all()
Expand All @@ -65,6 +72,12 @@ def test_reverse_foreign_key_retrieve(self):
]
self.assertEqual(serializer.data, expected)

def test_reverse_foreign_key_retrieve_prefetch_related(self):
queryset = ForeignKeyTarget.objects.all().prefetch_related('sources')
serializer = ForeignKeyTargetSerializer(queryset, many=True)
with self.assertNumQueries(2):
serializer.data

def test_foreign_key_update(self):
data = {'id': 1, 'name': 'source-1', 'target': 'target-2'}
instance = ForeignKeySource.objects.get(pk=1)
Expand Down