summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJim Bailey <jim@potatolondon.com>2013-11-01 12:55:35 +0000
committerAnssi Kääriäinen <akaariai@gmail.com>2013-11-03 12:27:54 +0200
commit539e3693d4712b249a95cfad8cfdeecdad1777a6 (patch)
tree6ef9c53c0f433430cfd1b60135cdcf31bb4fc684
parent4202d9cf0c359b5bb41247905454572b7f9644d9 (diff)
Fixed #20849 -- ModelForms do not work well with prefetch_related.
model_to_dict() (used when rendering forms) queries the database to get the list of primary keys for ManyToMany fields. This is unnecessary if the field queryset has been prefetched, all the keys are already in memory and can be obtained with a simple iteration.
-rw-r--r--django/forms/models.py6
-rw-r--r--tests/model_forms/tests.py35
2 files changed, 40 insertions, 1 deletions
diff --git a/django/forms/models.py b/django/forms/models.py
index 71da43390c..32f0f043ae 100644
--- a/django/forms/models.py
+++ b/django/forms/models.py
@@ -134,7 +134,11 @@ def model_to_dict(instance, fields=None, exclude=None):
data[f.name] = []
else:
# MultipleChoiceWidget needs a list of pks, not object instances.
- data[f.name] = list(f.value_from_object(instance).values_list('pk', flat=True))
+ qs = f.value_from_object(instance)
+ if qs._result_cache is not None:
+ data[f.name] = [item.pk for item in qs]
+ else:
+ data[f.name] = list(qs.values_list('pk', flat=True))
else:
data[f.name] = f.value_from_object(instance)
return data
diff --git a/tests/model_forms/tests.py b/tests/model_forms/tests.py
index ddc7a4ceef..4b6c3b36ad 100644
--- a/tests/model_forms/tests.py
+++ b/tests/model_forms/tests.py
@@ -824,6 +824,41 @@ class ModelToDictTests(TestCase):
# Ensure many-to-many relation appears as a list
self.assertIsInstance(d['categories'], list)
+ def test_reuse_prefetched(self):
+ # model_to_dict should not hit the database if it can reuse
+ # the data populated by prefetch_related.
+ categories = [
+ Category(name='TestName1', slug='TestName1', url='url1'),
+ Category(name='TestName2', slug='TestName2', url='url2'),
+ Category(name='TestName3', slug='TestName3', url='url3')
+ ]
+ for c in categories:
+ c.save()
+ writer = Writer(name='Test writer')
+ writer.save()
+
+ art = Article(
+ headline='Test article',
+ slug='test-article',
+ pub_date=datetime.date(1988, 1, 4),
+ writer=writer,
+ article='Hello.'
+ )
+ art.save()
+ for c in categories:
+ art.categories.add(c)
+
+ art = Article.objects.prefetch_related('categories').get(pk=art.pk)
+
+ with self.assertNumQueries(0):
+ d = model_to_dict(art)
+
+ #Ensure all many-to-many categories appear in model_to_dict
+ for c in categories:
+ self.assertIn(c.pk, d['categories'])
+ #Ensure many-to-many relation appears as a list
+ self.assertIsInstance(d['categories'], list)
+
class OldFormForXTests(TestCase):
def test_base_form(self):
self.assertEqual(Category.objects.count(), 0)