From 757945b47de45b6eaa0b0e14f87936261a60d227 Mon Sep 17 00:00:00 2001 From: Loic Bistuer Date: Sat, 2 Nov 2013 18:57:35 -0500 Subject: [PATCH 1/5] Fixed failing test around DST change. The timezone arithmetic done in JS can be off by one hour around DST change. We work around this issue by adding one extra hour to the test error margin when we detect a DST change is near. Refs #20663. --- tests/admin_widgets/tests.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/tests/admin_widgets/tests.py b/tests/admin_widgets/tests.py index 0aa8e989331..e6f33b04e04 100644 --- a/tests/admin_widgets/tests.py +++ b/tests/admin_widgets/tests.py @@ -2,7 +2,12 @@ from __future__ import unicode_literals from datetime import datetime, timedelta -from unittest import TestCase +from unittest import TestCase, skipIf + +try: + import pytz +except ImportError: + pytz = None from django import forms from django.conf import settings @@ -635,6 +640,7 @@ class DateTimePickerSeleniumIETests(DateTimePickerSeleniumFirefoxTests): webdriver_class = 'selenium.webdriver.ie.webdriver.WebDriver' +@skipIf(pytz is None, "this test requires pytz") @override_settings(TIME_ZONE='Asia/Singapore') @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',)) class DateTimePickerShortcutsSeleniumFirefoxTests(AdminSeleniumWebDriverTestCase): @@ -654,9 +660,18 @@ class DateTimePickerShortcutsSeleniumFirefoxTests(AdminSeleniumWebDriverTestCase """ self.admin_login(username='super', password='secret', login_url='/') - now = datetime.now() error_margin = timedelta(seconds=10) + # If we are neighbouring a DST, we add an hour of error margin. + tz = pytz.timezone('America/Chicago') + utc_now = datetime.now(pytz.utc) + tz_yesterday = (utc_now - timedelta(days=1)).astimezone(tz).tzname() + tz_tomorrow = (utc_now + timedelta(days=1)).astimezone(tz).tzname() + if tz_yesterday != tz_tomorrow: + error_margin += timedelta(hours=1) + + now = datetime.now() + self.selenium.get('%s%s' % (self.live_server_url, '/admin_widgets/member/add/')) From 6c1b55d61a3b2c5e401bf63c1c65d569217a130b Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Sun, 3 Nov 2013 01:52:40 -0700 Subject: [PATCH 2/5] Re-expose Count here --- django/contrib/gis/db/models/sql/aggregates.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/django/contrib/gis/db/models/sql/aggregates.py b/django/contrib/gis/db/models/sql/aggregates.py index f8f3ed1eb45..5a2e5d07bef 100644 --- a/django/contrib/gis/db/models/sql/aggregates.py +++ b/django/contrib/gis/db/models/sql/aggregates.py @@ -1,7 +1,9 @@ -from django.db.models.sql.aggregates import Aggregate +from django.db.models.sql.aggregates import Aggregate, Count from django.contrib.gis.db.models.fields import GeometryField +__all__ = ['Count', 'Collect', 'Extent', 'Extent3D', 'MakeLine', 'Union'] + class GeoAggregate(Aggregate): # Default SQL template for spatial aggregates. sql_template = '%(function)s(%(field)s)' From 4202d9cf0c359b5bb41247905454572b7f9644d9 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Sun, 3 Nov 2013 01:53:28 -0700 Subject: [PATCH 3/5] Whitespace fix --- django/contrib/gis/db/models/sql/aggregates.py | 1 + 1 file changed, 1 insertion(+) diff --git a/django/contrib/gis/db/models/sql/aggregates.py b/django/contrib/gis/db/models/sql/aggregates.py index 5a2e5d07bef..68fc12e54c8 100644 --- a/django/contrib/gis/db/models/sql/aggregates.py +++ b/django/contrib/gis/db/models/sql/aggregates.py @@ -4,6 +4,7 @@ from django.contrib.gis.db.models.fields import GeometryField __all__ = ['Count', 'Collect', 'Extent', 'Extent3D', 'MakeLine', 'Union'] + class GeoAggregate(Aggregate): # Default SQL template for spatial aggregates. sql_template = '%(function)s(%(field)s)' From 539e3693d4712b249a95cfad8cfdeecdad1777a6 Mon Sep 17 00:00:00 2001 From: Jim Bailey Date: Fri, 1 Nov 2013 12:55:35 +0000 Subject: [PATCH 4/5] 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. --- django/forms/models.py | 6 +++++- tests/model_forms/tests.py | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/django/forms/models.py b/django/forms/models.py index 71da43390c5..32f0f043ae6 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 ddc7a4ceefb..4b6c3b36adc 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) From f40f90d63b4be295269b5f0de5f919be22daba5c Mon Sep 17 00:00:00 2001 From: Florian Apolloner Date: Sun, 3 Nov 2013 12:45:15 +0100 Subject: [PATCH 5/5] Fixed regressions from 36ded01527b690b5df0574492af9cfcc2ea3d1dc. Refs #21302 --- django/contrib/gis/db/models/sql/aggregates.py | 5 +++-- django/db/models/sql/aggregates.py | 4 ++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/django/contrib/gis/db/models/sql/aggregates.py b/django/contrib/gis/db/models/sql/aggregates.py index 68fc12e54c8..c0a7d894ebc 100644 --- a/django/contrib/gis/db/models/sql/aggregates.py +++ b/django/contrib/gis/db/models/sql/aggregates.py @@ -1,8 +1,9 @@ -from django.db.models.sql.aggregates import Aggregate, Count +from django.db.models.sql import aggregates +from django.db.models.sql.aggregates import * # NOQA from django.contrib.gis.db.models.fields import GeometryField -__all__ = ['Count', 'Collect', 'Extent', 'Extent3D', 'MakeLine', 'Union'] +__all__ = ['Collect', 'Extent', 'Extent3D', 'MakeLine', 'Union'] + aggregates.__all__ class GeoAggregate(Aggregate): diff --git a/django/db/models/sql/aggregates.py b/django/db/models/sql/aggregates.py index 0e1794e0a9d..8542a330c64 100644 --- a/django/db/models/sql/aggregates.py +++ b/django/db/models/sql/aggregates.py @@ -5,6 +5,10 @@ import copy from django.db.models.fields import IntegerField, FloatField + +__all__ = ['Aggregate', 'Avg', 'Count', 'Max', 'Min', 'StdDev', 'Sum', 'Variance'] + + # Fake fields used to identify aggregate types in data-conversion operations. ordinal_aggregate_field = IntegerField() computed_aggregate_field = FloatField()