Commits

adamv committed b943b65

* Some style changes
* Bug 009: ado layer bombing out on NULL parameters.

  • Participants
  • Parent commits 90c2b9d

Comments (0)

Files changed (8)

File src/sqlserver_ado/adodb_django.py

     raise errorclass(errorvalue)
 
 
-class Error(exceptions.StandardError):
-    pass
-
-class Warning(exceptions.StandardError):
-    pass
+class Error(exceptions.StandardError): pass
+class Warning(exceptions.StandardError): pass
 
 class InterfaceError(Error):
     def __init__(self, inner_exception=None):
             s += "\n" + str(self.inner_exception)
         return s
 
-class DatabaseError(Error):
-    pass
+class DatabaseError(Error): pass
+class InternalError(DatabaseError): pass
+class OperationalError(DatabaseError): pass
+class ProgrammingError(DatabaseError): pass
+class IntegrityError(DatabaseError): pass
+class DataError(DatabaseError): pass
+class NotSupportedError(DatabaseError): pass
 
-class InternalError(DatabaseError):
-    pass
 
-class OperationalError(DatabaseError):
-    pass
+class DBAPITypeObject:
+  def __init__(self,valuesTuple):
+    self.values = valuesTuple
 
-class ProgrammingError(DatabaseError):
-    pass
+  def __cmp__(self,other):
+    if other in self.values:
+      return 0
+      
+    if other < self.values:
+      return 1
 
-class IntegrityError(DatabaseError):
-    pass
+    return -1
 
-class DataError(DatabaseError):
-    pass
-
-class NotSupportedError(DatabaseError):
-    pass
 
 def _logger(message, log_level=1):
 	if verbose and verbose>=log_level: print message
 
     def _returnADOCommandParameters(self,adoCommand):
         retLst = []
-        for i in range(adoCommand.Parameters.Count):
-            p = adoCommand.Parameters(i)
+        for p in adoCommand.Parameters:
             if verbose > 2:
                 print 'return', p.Name, p.Type, p.Direction, repr(p.Value)
             pyObject = convertVariantToPython(p.Value,p.Type)
                         p.Size = len(s)
                         
                     elif isinstance(elem, basestring):
+                    	
                         s = elem
                         # Hack to trim microseconds on iso dates down to 3 decimals
                         try: # ... only if parameter is a datetime string
     return decimal.Decimal((long(hi) << 32) + lo)/decimal.Decimal(1000)
 
 def cvtNumeric(variant):
-    try:
-        return decimal.Decimal(variant)
-    except (ValueError,TypeError):
-        pass
-    try:
-        europeVsUS=str(variant).replace(",",".")
-        return decimal.Decimal(europeVsUS)
-    except (ValueError,TypeError):
-        pass
+	return _convertNumberWithCulture(variant, decimal.Decimal)
 
 def cvtFloat(variant):
+	return _convertNumberWithCulture(variant, float)
+        
+def _convertNumberWithCulture(variant, f):
     try:
-        return float(variant)
-    except (ValueError,TypeError):
-        pass
+        return f(variant)
+    except (ValueError,TypeError): pass
 
     try:
         europeVsUS = str(variant).replace(",",".")
-        return float(europeVsUS)
-    except (ValueError,TypeError):
-        pass
+        return f(europeVsUS)
+    except (ValueError,TypeError): pass
+	
 
 def convertVariantToPython(variant, adType):
     if variant is None:
     return converted
 
 
-class DBAPITypeObject:
-  def __init__(self,valuesTuple):
-    self.values = valuesTuple
-
-  def __cmp__(self,other):
-    if other in self.values:
-      return 0
-    if other < self.values:
-      return 1
-    else:
-      return -1
-
-adoIntegerTypes=(adInteger,adSmallInt,adTinyInt,adUnsignedInt,
+adoIntegerTypes = (adInteger,adSmallInt,adTinyInt,adUnsignedInt,
                  adUnsignedSmallInt,adUnsignedTinyInt,
                  adBoolean,adError)
-adoRowIdTypes=(adChapter,)
-adoLongTypes=(adBigInt,adFileTime,adUnsignedBigInt)
-adoExactNumericTypes=(adDecimal,adNumeric,adVarNumeric,adCurrency)
-adoApproximateNumericTypes=(adDouble,adSingle)
-adoStringTypes=(adBSTR,adChar,adLongVarChar,adLongVarWChar,
+adoRowIdTypes = (adChapter,)
+adoLongTypes = (adBigInt,adFileTime,adUnsignedBigInt)
+adoExactNumericTypes = (adDecimal,adNumeric,adVarNumeric,adCurrency)
+adoApproximateNumericTypes = (adDouble,adSingle)
+adoStringTypes = (adBSTR,adChar,adLongVarChar,adLongVarWChar,
                 adVarChar,adVarWChar,adWChar,adGUID)
-adoBinaryTypes=(adBinary,adLongVarBinary,adVarBinary)
-adoDateTimeTypes=(adDBTime, adDBTimeStamp, adDate, adDBDate)
-adoRemainingTypes=(adEmpty,adIDispatch,adIUnknown,
+adoBinaryTypes = (adBinary,adLongVarBinary,adVarBinary)
+adoDateTimeTypes = (adDBTime, adDBTimeStamp, adDate, adDBDate)
+adoRemainingTypes = (adEmpty,adIDispatch,adIUnknown,
                    adPropVariant,adArray,adUserDefined,
                    adVariant)
 
 BINARY   = DBAPITypeObject(adoBinaryTypes)
 
 """This type object is used to describe numeric columns in a database. """
-NUMBER   = DBAPITypeObject(adoIntegerTypes + adoLongTypes + \
-                           adoExactNumericTypes + adoApproximateNumericTypes)
+NUMBER   = DBAPITypeObject(adoIntegerTypes + adoLongTypes + adoExactNumericTypes + adoApproximateNumericTypes)
 
 """This type object is used to describe date/time columns in a database. """
 DATETIME = DBAPITypeObject(adoDateTimeTypes)
 	long: adBigInt,
 	str: adBSTR,
 	unicode: adBSTR,
-	type(None): adEmpty,
 	bool: adBoolean,
 	decimal.Decimal: adNumeric,
 	datetime.date: adDate,
 	datetime.datetime: adDate,
 	datetime.time: adDate,
+	
+	# This is causing problems when "adEmpty", as that's not a SQL command parameter type.
+	# So, fake a type that's more or less convertable.
+	# (Alternate approach would be to replace ? in query with literal NULLs...)
+	type(None): adBSTR,
 }
 
 class VariantConversionMap(object):
     def __init__(self, mapping):
     	self.storage = dict()
     	
-    	# mapping is a dict of tuple(ado Type) => convert function
-    	for adoType_list, convert_function in mapping.iteritems():
+    	# mapping is a dict of: tuple(ado Type) => conversion function
+    	for adoType_list, conversion_function in mapping.iteritems():
     		for adoType in adoType_list:
-    			self.storage[adoType] = convert_function
+    			self.storage[adoType] = conversion_function
 
     def __getitem__(self, key):
     	return self.storage.get(key, identity)

File tests/test_bug_009/__init__.py

Empty file added.

File tests/test_bug_009/insert_nulls.py

+from myapp.models import *
+obj1 = SomeTable(amount=47)
+obj1.save()
+
+obj2 = SomeTable(amount=None)
+obj2.save()

File tests/test_bug_009/manage.py

+#!/usr/bin/env python
+from django.core.management import execute_manager
+try:
+    import settings # Assumed to be in the same directory.
+except ImportError:
+    import sys
+    sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__)
+    sys.exit(1)
+
+if __name__ == "__main__":
+    execute_manager(settings)

File tests/test_bug_009/myapp/__init__.py

Empty file added.

File tests/test_bug_009/myapp/models.py

+from django.db import models
+
+class TableNullText(models.Model):
+	amount = models.TextField(null=True)
+	
+	def __unicode__(self):
+		return u'Amount: ' + unicode(self.amount)
+		
+class TableNullInteger(models.Model):
+	amount = models.IntegerField(null=True)
+	
+	def __unicode__(self):
+		return u'Amount: ' + unicode(self.amount)
+
+class TableNullDate(models.Model):
+	amount = models.DateTimeField(null=True)
+	
+	def __unicode__(self):
+		return u'Amount: ' + unicode(self.amount)

File tests/test_bug_009/settings.py

+# Django settings for testbackend project.
+def hack_backend_path():
+	import os, sys
+	backend_path = os.path.join(os.path.abspath(os.path.dirname(".")), "../../src")
+	sys.path.append(backend_path)
+	
+hack_backend_path()
+
+
+DEBUG = True
+TEMPLATE_DEBUG = DEBUG
+
+ADMINS = (
+    # ('Your Name', 'your_email@domain.com'),
+)
+
+MANAGERS = ADMINS
+
+DATABASE_ENGINE = 'sqlserver_ado'
+DATABASE_MSSQL_REGEX = True
+
+DATABASE_HOST = r'localhost\ss2005'
+DATABASE_PORT = ''
+DATABASE_NAME = r'django_test_backend'
+
+# Use integrated auth.
+DATABASE_USER = ''
+DATABASE_PASSWORD = ''
+
+
+# Local time zone for this installation. Choices can be found here:
+# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
+# although not all choices may be available on all operating systems.
+# If running in a Windows environment this must be set to the same as your
+# system time zone.
+TIME_ZONE = 'America/New York'
+
+# Language code for this installation. All choices can be found here:
+# http://www.i18nguy.com/unicode/language-identifiers.html
+LANGUAGE_CODE = 'en-us'
+
+SITE_ID = 1
+
+# If you set this to False, Django will make some optimizations so as not
+# to load the internationalization machinery.
+USE_I18N = True
+
+# Absolute path to the directory that holds media.
+# Example: "/home/media/media.lawrence.com/"
+MEDIA_ROOT = ''
+
+# URL that handles the media served from MEDIA_ROOT. Make sure to use a
+# trailing slash if there is a path component (optional in other cases).
+# Examples: "http://media.lawrence.com", "http://example.com/media/"
+MEDIA_URL = ''
+
+# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a
+# trailing slash.
+# Examples: "http://foo.com/media/", "/media/".
+ADMIN_MEDIA_PREFIX = '/media/'
+
+# Make this unique, and don't share it with anybody.
+SECRET_KEY = '%(u8uds4xsy0+95cj3o&k49*u@&--yp0t&e&0$!@s2fvea#u4j'
+
+# List of callables that know how to import templates from various sources.
+TEMPLATE_LOADERS = (
+    'django.template.loaders.filesystem.load_template_source',
+    'django.template.loaders.app_directories.load_template_source',
+)
+
+MIDDLEWARE_CLASSES = (
+    'django.middleware.common.CommonMiddleware',
+    'django.contrib.sessions.middleware.SessionMiddleware',
+    'django.contrib.auth.middleware.AuthenticationMiddleware',
+    'django.middleware.doc.XViewMiddleware',
+)
+
+ROOT_URLCONF = 'urls'
+
+TEMPLATE_DIRS = (
+    # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
+    # Always use forward slashes, even on Windows.
+    # Don't forget to use absolute paths, not relative paths.
+)
+
+INSTALLED_APPS = (
+	'django.contrib.admin',
+	'django.contrib.auth',
+	'django.contrib.contenttypes',
+	'django.contrib.sessions',
+	'django.contrib.sites',
+	'myapp',
+)

File tests/test_bug_009/urls.py

+from django.conf.urls.defaults import *
+
+urlpatterns = patterns('',
+    # Example:
+    # (r'^test_models/', include('test_models.foo.urls')),
+
+    # Uncomment this for admin:
+     (r'^admin/', include('django.contrib.admin.urls')),
+)