From 9c562eb419e84ee9c681546b7db1feb0d8fb8363 Mon Sep 17 00:00:00 2001
From: Hugo Rodger-Brown <hugo@rodger-brown.com>
Date: Thu, 28 Nov 2013 17:49:56 +0000
Subject: [PATCH] =?UTF-8?q?Fixed=20#21523=20=E2=80=94=20added=20additional=20?=
=?UTF-8?q?parsing=20to=20DateField=20to=5Fpython=20method.?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The to_python method attempts to convert a DateField value to a python
datetime.date object instance. It checks for datetime.date, datetime.
datetime instances, and if neither of those it then attempts to parse
the value as a string. This can fail if the value is an object that is
not a date, but is being used to represent one - i.e. a mock object.
Previously there was a call to django.utils.encoding.smart_str before
the call to parse_date, which essentially converted the value to its
unicode representation. This was removed when the smart_str method was
removed (in favour of smart_text). I have put a call to smart_text back
in.
---
django/db/models/fields/__init__.py | 9 +++++++++
tests/regressiontests/model_fields/tests.py | 15 +++++++++++++++
2 files changed, 24 insertions(+)
diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py
index 9949dfa..069e565 100644
a
|
b
|
class DateField(Field):
|
693 | 693 | if isinstance(value, datetime.date): |
694 | 694 | return value |
695 | 695 | |
| 696 | # handle objects that are neither dates nor strings, but which |
| 697 | # return a parseable date string, e.g. a mock date class. |
| 698 | value = smart_text(value) |
| 699 | |
696 | 700 | try: |
697 | 701 | parsed = parse_date(value) |
698 | 702 | if parsed is not None: |
… |
… |
class DateTimeField(DateField):
|
784 | 788 | value = timezone.make_aware(value, default_timezone) |
785 | 789 | return value |
786 | 790 | |
| 791 | |
| 792 | # handle objects that are neither dates nor strings, but which |
| 793 | # return a parseable date string, e.g. a mock date class. |
| 794 | value = smart_text(value) |
| 795 | |
787 | 796 | try: |
788 | 797 | parsed = parse_datetime(value) |
789 | 798 | if parsed is not None: |
diff --git a/tests/regressiontests/model_fields/tests.py b/tests/regressiontests/model_fields/tests.py
index a49894e..eb1a5fd6 100644
a
|
b
|
|
1 | 1 | from __future__ import absolute_import, unicode_literals |
2 | 2 | |
| 3 | import mock |
3 | 4 | import datetime |
4 | 5 | from decimal import Decimal |
5 | 6 | |
… |
… |
class ForeignKeyTests(test.TestCase):
|
127 | 128 | b = Bar.objects.create(b="bcd") |
128 | 129 | self.assertEqual(b.a, a) |
129 | 130 | |
| 131 | from datetime import date as real_date # used to test mocking dates. |
130 | 132 | class DateTimeFieldTests(unittest.TestCase): |
| 133 | |
| 134 | class FakeDate(real_date): |
| 135 | "An object that is *not* a date, but which could be parsed as such." |
| 136 | def __new__(cls, *args, **kwargs): |
| 137 | return real_date.__new__(real_date, *args, **kwargs) |
| 138 | |
131 | 139 | def test_datetimefield_to_python_usecs(self): |
132 | 140 | """DateTimeField.to_python should support usecs""" |
133 | 141 | f = models.DateTimeField() |
… |
… |
class DateTimeFieldTests(unittest.TestCase):
|
144 | 152 | self.assertEqual(f.to_python('01:02:03.999999'), |
145 | 153 | datetime.time(1, 2, 3, 999999)) |
146 | 154 | |
| 155 | @mock.patch('datetime.date', FakeDate) |
| 156 | def test_datefield_to_python_can_be_mocked(self): |
| 157 | """DateField.to_python should support mock dates.""" |
| 158 | self.assertEqual(models.DateField().to_python( |
| 159 | DateTimeFieldTests.FakeDate.today()), |
| 160 | datetime.date.today()) |
| 161 | |
147 | 162 | class BooleanFieldTests(unittest.TestCase): |
148 | 163 | def _test_get_db_prep_lookup(self, f): |
149 | 164 | from django.db import connection |