﻿id	summary	reporter	owner	description	type	status	component	version	severity	resolution	keywords	cc	stage	has_patch	needs_docs	needs_tests	needs_better_patch	easy	ui_ux
12818	sqlite3 backend should use single quotes for string values passed as args to django_date_trunc and django_extract	Lakin Wecker	Chris Beaven	"Test case models.py:
{{{
from django.db import models

#------------------------------------------------------------------------------
class SimpleModel(models.Model):
    month = models.PositiveIntegerField()
    some_date = models.DateTimeField()

#------------------------------------------------------------------------------
class SecondModel(models.Model):
    year = models.PositiveIntegerField()
    some_date = models.DateTimeField()

}}}

Test case admin.py:
{{{
from django.contrib import admin
from datehierarchy.testbed.models import SimpleModel, SecondModel

#-------------------------------------------------------------------------------
class SimpleModelAdmin(admin.ModelAdmin):
    list_display = ('month', 'some_date',)
    date_hierarchy = 'some_date'

admin.site.register(SimpleModel, SimpleModelAdmin)


#-------------------------------------------------------------------------------
class SecondModelAdmin(admin.ModelAdmin):
    list_display = ('year', 'some_date',)
    date_hierarchy = 'some_date'

# Content Models
admin.site.register(SecondModel, SecondModelAdmin)
}}}

Produces tracebacks like this even if valid dates are present:
{{{
Traceback:
File ""/home/lakin/Desktop/django/core/handlers/base.py"" in get_response
  92.                 response = callback(request, *callback_args, **callback_kwargs)
File ""/home/lakin/Desktop/django/contrib/admin/options.py"" in wrapper
  226.                 return self.admin_site.admin_view(view)(*args, **kwargs)
File ""/home/lakin/Desktop/django/views/decorators/cache.py"" in _wrapped_view_func
  44.         response = view_func(request, *args, **kwargs)
File ""/home/lakin/Desktop/django/contrib/admin/sites.py"" in inner
  186.             return view(request, *args, **kwargs)
File ""/home/lakin/Desktop/django/contrib/admin/options.py"" in changelist_view
  986.         ], context, context_instance=context_instance)
File ""/home/lakin/Desktop/django/shortcuts/__init__.py"" in render_to_response
  20.     return HttpResponse(loader.render_to_string(*args, **kwargs), **httpresponse_kwargs)
File ""/home/lakin/Desktop/django/template/loader.py"" in render_to_string
  108.     return t.render(context_instance)
File ""/home/lakin/Desktop/django/template/__init__.py"" in render
  178.         return self.nodelist.render(context)
File ""/home/lakin/Desktop/django/template/__init__.py"" in render
  779.                 bits.append(self.render_node(node, context))
File ""/home/lakin/Desktop/django/template/debug.py"" in render_node
  71.             result = node.render(context)
File ""/home/lakin/Desktop/django/template/loader_tags.py"" in render
  97.         return compiled_parent.render(context)
File ""/home/lakin/Desktop/django/template/__init__.py"" in render
  178.         return self.nodelist.render(context)
File ""/home/lakin/Desktop/django/template/__init__.py"" in render
  779.                 bits.append(self.render_node(node, context))
File ""/home/lakin/Desktop/django/template/debug.py"" in render_node
  71.             result = node.render(context)
File ""/home/lakin/Desktop/django/template/loader_tags.py"" in render
  97.         return compiled_parent.render(context)
File ""/home/lakin/Desktop/django/template/__init__.py"" in render
  178.         return self.nodelist.render(context)
File ""/home/lakin/Desktop/django/template/__init__.py"" in render
  779.                 bits.append(self.render_node(node, context))
File ""/home/lakin/Desktop/django/template/debug.py"" in render_node
  71.             result = node.render(context)
File ""/home/lakin/Desktop/django/template/loader_tags.py"" in render
  24.         result = self.nodelist.render(context)
File ""/home/lakin/Desktop/django/template/__init__.py"" in render
  779.                 bits.append(self.render_node(node, context))
File ""/home/lakin/Desktop/django/template/debug.py"" in render_node
  71.             result = node.render(context)
File ""/home/lakin/Desktop/django/template/loader_tags.py"" in render
  24.         result = self.nodelist.render(context)
File ""/home/lakin/Desktop/django/template/__init__.py"" in render
  779.                 bits.append(self.render_node(node, context))
File ""/home/lakin/Desktop/django/template/debug.py"" in render_node
  71.             result = node.render(context)
File ""/home/lakin/Desktop/django/template/__init__.py"" in render
  936.                     dict = func(*args)
File ""/home/lakin/Desktop/django/contrib/admin/templatetags/admin_list.py"" in date_hierarchy
  313.                 } for year in years]
File ""/home/lakin/Desktop/django/db/models/query.py"" in _result_iter
  106.                 self._fill_cache()
File ""/home/lakin/Desktop/django/db/models/query.py"" in _fill_cache
  692.                     self._result_cache.append(self._iter.next())
File ""/home/lakin/Desktop/django/db/models/sql/subqueries.py"" in results_iter
  397.                     date = typecast_timestamp(str(date))
File ""/home/lakin/Desktop/django/db/backends/util.py"" in typecast_timestamp
  68.     if not ' ' in s: return typecast_date(s)
File ""/home/lakin/Desktop/django/db/backends/util.py"" in typecast_date
  53.     return s and datetime.date(*map(int, s.split('-'))) or None # returns None if s is null

Exception Type: ValueError at /admin/testbed/secondmodel/
Exception Value: invalid literal for int() with base 10: 'None'
}}}

This is due to the use of double quotes in queries like the following:

{{{
SELECT DISTINCT django_date_trunc('year', ""testbed_secondmodel"".""some_date"") FROM ""testbed_secondmodel"" ORDER BY 1 ASC
}}}

Double quotes refer to column names, and int he above test case, the table testbed_secondmodel has a column named {{{year}}}, and as such the value of {{{year}}} is passed into the the python function {{{_sqlite_date_trunc}}} - instead of the string {{{""year""}}}.  Instead, if one uses single quotes as per the string constant section in http://www.sqlite.org/lang_expr.html - it works just fine.

Possible patch:


{{{
Index: sqlite3/base.py
===================================================================
--- sqlite3/base.py	(revision 12398)
+++ sqlite3/base.py	(working copy)
@@ -65,12 +65,12 @@
     def date_extract_sql(self, lookup_type, field_name):
         # sqlite doesn't support extract, so we fake it with the user-defined
         # function django_extract that's registered in connect().
-        return 'django_extract(""%s"", %s)' % (lookup_type.lower(), field_name)
+        return 'django_extract(\'%s\', %s)' % (lookup_type.lower(), field_name)
 
     def date_trunc_sql(self, lookup_type, field_name):
         # sqlite doesn't support DATE_TRUNC, so we fake it with a user-defined
         # function django_date_trunc that's registered in connect().
-        return 'django_date_trunc(""%s"", %s)' % (lookup_type.lower(), field_name)
+        return 'django_date_trunc(\'%s\', %s)' % (lookup_type.lower(), field_name)
 
     def drop_foreignkey_sql(self):
         return """"
}}} "		closed	Database layer (models, ORM)	1.1		fixed		lakin@…	Ready for checkin	1	0	0	0	0	0
