Package: python-django
Severity: normal
Tags: patch
Upstream bug: https://code.djangoproject.com/ticket/28549
From the upstream bug report:
-
Using the models:
from django.db import models
class Base(models.Model):
f1 = models.CharField(max_length=10)
class Sub(Base):
f2 = models.CharField(max_length=10)
it seems that I can't defer() both f1 and f2 in a single query:
Sub.objects.defer('f1', 'f2').query.sql_with_params()[0]
u'SELECT "defer_base"."id", "defer_base"."f1", "defer_sub"."base_ptr_id"
FROM "defer_sub" INNER JOIN "defer_base" ON ("defer_sub"."base_ptr_id" =
"defer_base"."id")'
(we're seeing f1 in the SELECT value list).
I seem to be able to defer f1 or f2 separately though.
I'm no django hacker, but: it seems as though
django.db.models.sql.query.Query.deferred_to_data() is iterating both
models in the loop:
#640:
for model, values in six.iteritems(seen):
for field in model._meta.fields:
if field in values:
continue
- and so is discovering f1 twice: once as Base.f1 and again as Sub.f1.
Since the field in values test only skips Base.f1, we're still left with
Sub.f1 in the workset dict.
-
This bug caused significant performance degradation when we upgraded a
Django application to a new version that relied on model inheritance.
The attached patch is a backport of commit
84b7cb7df00192b2f804e2c6fd98b78b5bfd1ffa from upstream master.
This issue applies to both 1:1.10.7-2 and 1:1.11.5-1.
Patch supplied by Jeremy Kerr and tested/backported by Daniel Axtens.
Regards,
--
Andrew Donnellan OzLabs, ADL Canberra
andrew.donnel...@au1.ibm.com IBM Australia Limited
>From ac607f20c2dbcd2cfd88fcd8b78259d75abdb7d4 Mon Sep 17 00:00:00 2001
From: Jeremy Kerr
Date: Thu, 31 Aug 2017 08:59:45 +0800
Subject: [PATCH] Fixed #28549 -- Fixed QuerySet.defer() with super and
subclass fields.
commit 84b7cb7df00192b2f804e2c6fd98b78b5bfd1ffa upstream.
Previously, deferring fields in different classes didn't omit the
superclass' deferred field.
Thanks Simon Charette for the suggested fix.
[ajd: backported on top of Debian tree]
Signed-off-by: Andrew Donnellan
---
django/db/models/sql/query.py | 2 +-
tests/defer/tests.py | 5 +
2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py
index e51b1037c..8813dce57 100644
--- a/django/db/models/sql/query.py
+++ b/django/db/models/sql/query.py
@@ -669,7 +669,7 @@ class Query(object):
# models.
workset = {}
for model, values in six.iteritems(seen):
-for field in model._meta.fields:
+for field in model._meta.local_fields:
if field in values:
continue
m = field.model._meta.concrete_model
diff --git a/tests/defer/tests.py b/tests/defer/tests.py
index 65f1f2bb1..ef7180a85 100644
--- a/tests/defer/tests.py
+++ b/tests/defer/tests.py
@@ -190,6 +190,11 @@ class BigChildDeferTests(AssertionMixin, TestCase):
self.assertEqual(obj.value, "foo")
self.assertEqual(obj.other, "bar")
+def test_defer_subclass_both(self):
+# Deferring fields from both superclass and subclass works.
+obj = BigChild.objects.defer("other", "value").get(name="b1")
+self.assert_delayed(obj, 2)
+
def test_only_baseclass_when_subclass_has_added_field(self):
# You can retrieve a single field on a baseclass
obj = BigChild.objects.only("name").get(name="b1")
--
2.11.0
___
Python-modules-team mailing list
Python-modules-team@lists.alioth.debian.org
http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/python-modules-team