Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed #16408 -- Fixed conversion of dates, and other problems with th…

…e SpatiaLite backend.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@16749 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 43329af2e3f99e6dc39c6ae390b055aa180554ad 1 parent 6ce463a
Justin Bronn authored September 09, 2011
9  django/contrib/gis/db/backends/spatialite/base.py
@@ -2,16 +2,16 @@
2 2
 from django.conf import settings
3 3
 
4 4
 from django.core.exceptions import ImproperlyConfigured
5  
-from django.db.backends.sqlite3.base import *
6 5
 from django.db.backends.sqlite3.base import (
7  
-    _sqlite_extract, _sqlite_date_trunc, _sqlite_regexp,
8  
-    DatabaseWrapper as SqliteDatabaseWrapper)
  6
+    _sqlite_extract, _sqlite_date_trunc, _sqlite_regexp, _sqlite_format_dtdelta,
  7
+    connection_created, Database, DatabaseWrapper as SQLiteDatabaseWrapper,
  8
+    SQLiteCursorWrapper)
9 9
 from django.contrib.gis.db.backends.spatialite.client import SpatiaLiteClient
10 10
 from django.contrib.gis.db.backends.spatialite.creation import SpatiaLiteCreation
11 11
 from django.contrib.gis.db.backends.spatialite.introspection import SpatiaLiteIntrospection
12 12
 from django.contrib.gis.db.backends.spatialite.operations import SpatiaLiteOperations
13 13
 
14  
-class DatabaseWrapper(SqliteDatabaseWrapper):
  14
+class DatabaseWrapper(SQLiteDatabaseWrapper):
15 15
     def __init__(self, *args, **kwargs):
16 16
         # Before we get too far, make sure pysqlite 2.5+ is installed.
17 17
         if Database.version_info < (2, 5, 0):
@@ -52,6 +52,7 @@ def _cursor(self):
52 52
             self.connection.create_function("django_extract", 2, _sqlite_extract)
53 53
             self.connection.create_function("django_date_trunc", 2, _sqlite_date_trunc)
54 54
             self.connection.create_function("regexp", 2, _sqlite_regexp)
  55
+            self.connection.create_function("django_format_dtdelta", 5, _sqlite_format_dtdelta)
55 56
             connection_created.send(sender=self.__class__, connection=self)
56 57
 
57 58
             ## From here on, customized for GeoDjango ##
32  django/contrib/gis/db/backends/spatialite/compiler.py
... ...
@@ -0,0 +1,32 @@
  1
+from django.db.backends.util import typecast_timestamp
  2
+from django.db.models.sql import compiler
  3
+from django.db.models.sql.constants import MULTI
  4
+from django.contrib.gis.db.models.sql.compiler import GeoSQLCompiler as BaseGeoSQLCompiler
  5
+
  6
+SQLCompiler = compiler.SQLCompiler
  7
+
  8
+class GeoSQLCompiler(BaseGeoSQLCompiler, SQLCompiler):
  9
+    pass
  10
+
  11
+class SQLInsertCompiler(compiler.SQLInsertCompiler, GeoSQLCompiler):
  12
+    pass
  13
+
  14
+class SQLDeleteCompiler(compiler.SQLDeleteCompiler, GeoSQLCompiler):
  15
+    pass
  16
+
  17
+class SQLUpdateCompiler(compiler.SQLUpdateCompiler, GeoSQLCompiler):
  18
+    pass
  19
+
  20
+class SQLAggregateCompiler(compiler.SQLAggregateCompiler, GeoSQLCompiler):
  21
+    pass
  22
+
  23
+class SQLDateCompiler(compiler.SQLDateCompiler, GeoSQLCompiler):
  24
+    """
  25
+    This is overridden for GeoDjango to properly cast date columns, see #16757.
  26
+    """
  27
+    def results_iter(self):
  28
+        offset = len(self.query.extra_select)
  29
+        for rows in self.execute_sql(MULTI):
  30
+            for row in rows:
  31
+                date = typecast_timestamp(str(row[offset]))
  32
+                yield date
46  django/contrib/gis/db/backends/spatialite/creation.py
@@ -3,7 +3,6 @@
3 3
 from django.core.cache import get_cache
4 4
 from django.core.cache.backends.db import BaseDatabaseCache
5 5
 from django.core.exceptions import ImproperlyConfigured
6  
-from django.core.management import call_command
7 6
 from django.db.backends.sqlite3.creation import DatabaseCreation
8 7
 
9 8
 class SpatiaLiteCreation(DatabaseCreation):
@@ -16,26 +15,65 @@ def create_test_db(self, verbosity=1, autoclobber=False):
16 15
         This method is overloaded to load up the SpatiaLite initialization
17 16
         SQL prior to calling the `syncdb` command.
18 17
         """
  18
+        # Don't import django.core.management if it isn't needed.
  19
+        from django.core.management import call_command
  20
+
  21
+        test_database_name = self._get_test_db_name()
  22
+
19 23
         if verbosity >= 1:
20  
-            print "Creating test database '%s'..." % self.connection.alias
  24
+            test_db_repr = ''
  25
+            if verbosity >= 2:
  26
+                test_db_repr = " ('%s')" % test_database_name
  27
+            print "Creating test database for alias '%s'%s..." % (self.connection.alias, test_db_repr)
21 28
 
22  
-        test_database_name = self._create_test_db(verbosity, autoclobber)
  29
+        self._create_test_db(verbosity, autoclobber)
23 30
 
24 31
         self.connection.close()
25  
-
26 32
         self.connection.settings_dict["NAME"] = test_database_name
  33
+
27 34
         # Confirm the feature set of the test database
28 35
         self.connection.features.confirm()
  36
+
29 37
         # Need to load the SpatiaLite initialization SQL before running `syncdb`.
30 38
         self.load_spatialite_sql()
31 39
         call_command('syncdb', verbosity=verbosity, interactive=False, database=self.connection.alias)
32 40
 
  41
+        # Report syncdb messages at one level lower than that requested.
  42
+        # This ensures we don't get flooded with messages during testing
  43
+        # (unless you really ask to be flooded)
  44
+        call_command('syncdb',
  45
+            verbosity=max(verbosity - 1, 0),
  46
+            interactive=False,
  47
+            database=self.connection.alias,
  48
+            load_initial_data=False)
  49
+
  50
+        # We need to then do a flush to ensure that any data installed by
  51
+        # custom SQL has been removed. The only test data should come from
  52
+        # test fixtures, or autogenerated from post_syncdb triggers.
  53
+        # This has the side effect of loading initial data (which was
  54
+        # intentionally skipped in the syncdb).
  55
+        call_command('flush',
  56
+            verbosity=max(verbosity - 1, 0),
  57
+            interactive=False,
  58
+            database=self.connection.alias)
  59
+
  60
+        # One effect of calling syncdb followed by flush is that the id of the
  61
+        # default site may or may not be 1, depending on how the sequence was
  62
+        # reset.  If the sites app is loaded, then we coerce it.
  63
+        from django.db.models import get_model
  64
+        Site = get_model('sites', 'Site')
  65
+        if Site is not None and Site.objects.using(self.connection.alias).count() == 1:
  66
+            Site.objects.using(self.connection.alias).update(id=settings.SITE_ID)
  67
+
  68
+        from django.core.cache import get_cache
  69
+        from django.core.cache.backends.db import BaseDatabaseCache
33 70
         for cache_alias in settings.CACHES:
34 71
             cache = get_cache(cache_alias)
35 72
             if isinstance(cache, BaseDatabaseCache):
36 73
                 from django.db import router
37 74
                 if router.allow_syncdb(self.connection.alias, cache.cache_model_class):
38 75
                     call_command('createcachetable', cache._table, database=self.connection.alias)
  76
+
39 77
         # Get a cursor (even though we don't need one yet). This has
40 78
         # the side effect of initializing the test database.
41 79
         cursor = self.connection.cursor()
2  django/contrib/gis/db/backends/spatialite/operations.py
@@ -48,7 +48,7 @@ def get_dist_ops(operator):
48 48
     return (SpatiaLiteDistance(operator),)
49 49
 
50 50
 class SpatiaLiteOperations(DatabaseOperations, BaseSpatialOperations):
51  
-    compiler_module = 'django.contrib.gis.db.models.sql.compiler'
  51
+    compiler_module = 'django.contrib.gis.db.backends.spatialite.compiler'
52 52
     name = 'spatialite'
53 53
     spatialite = True
54 54
     version_regex = re.compile(r'^(?P<major>\d)\.(?P<minor1>\d)\.(?P<minor2>\d+)')
2  django/contrib/gis/db/models/sql/compiler.py
@@ -202,7 +202,7 @@ def resolve_columns(self, row, fields=()):
202 202
     #### Routines unique to GeoQuery ####
203 203
     def get_extra_select_format(self, alias):
204 204
         sel_fmt = '%s'
205  
-        if alias in self.query.custom_select:
  205
+        if hasattr(self.query, 'custom_select') and alias in self.query.custom_select:
206 206
             sel_fmt = sel_fmt % self.query.custom_select[alias]
207 207
         return sel_fmt
208 208
 
1  django/contrib/gis/tests/geoapp/models.py
@@ -19,6 +19,7 @@ def __unicode__(self): return self.name
19 19
 # This is an inherited model from City
20 20
 class PennsylvaniaCity(City):
21 21
     county = models.CharField(max_length=30)
  22
+    founded = models.DateTimeField(null=True)
22 23
     objects = models.GeoManager() # TODO: This should be implicitly inherited.
23 24
 
24 25
 class State(models.Model):
14  django/contrib/gis/tests/geoapp/test_regress.py
... ...
@@ -1,9 +1,10 @@
1  
-import unittest
  1
+from datetime import datetime
2 2
 from django.contrib.gis.tests.utils import no_mysql, no_spatialite
3 3
 from django.contrib.gis.shortcuts import render_to_kmz
4  
-from models import City
  4
+from django.test import TestCase
  5
+from models import City, PennsylvaniaCity
5 6
 
6  
-class GeoRegressionTests(unittest.TestCase):
  7
+class GeoRegressionTests(TestCase):
7 8
 
8 9
     def test01_update(self):
9 10
         "Testing GeoQuerySet.update(), see #10411."
@@ -35,3 +36,10 @@ def test03_extent(self):
35 36
         extent = City.objects.filter(name='Pueblo').extent()
36 37
         for ref_val, val in zip(ref_ext, extent):
37 38
             self.assertAlmostEqual(ref_val, val, 4)
  39
+
  40
+    def test04_unicode_date(self):
  41
+        "Testing dates are converted properly, even on SpatiaLite, see #16408."
  42
+        founded = datetime(1857, 5, 23)
  43
+        mansfield = PennsylvaniaCity.objects.create(name='Mansfield', county='Tioga', point='POINT(-77.071445 41.823881)',
  44
+                                                    founded=founded)
  45
+        self.assertEqual(founded, PennsylvaniaCity.objects.dates('founded', 'day')[0])

0 notes on commit 43329af

Please sign in to comment.
Something went wrong with that request. Please try again.