Commits

Michael Manfre committed f4838fc

Issue 75 - Django 1.2 multi-db support

Comments (0)

Files changed (8)

 
 == TO INSTALL ==
 
-Copy the entire "sqlserver_ado" folder from src/ to somewhere on your Python 
+Copy the entire "sqlserver_ado" folder from source/ to somewhere on your Python 
 path, visible to both Django and the command-line tools. This could be in 
 your lib/site_packages, django/db/backends (if you don't mind mixing in 
 external code), or anywhere else on the Python path.
 
+== Django Version ==
+
+This version of Django-mssql requires Django version 1.2+ due to the database 
+backend changes made to support django multi-db support.
+
 == References ==
 
   * Project site: http://code.google.com/p/django-mssql/

source/sqlserver_ado/base.py

             self._dict = mapping
             
         def __getattr__(self, name):
-            if hasattr(self._dict, "get"):
-                return self._dict.get(name)
+            d = self._dict
+            result = None
+            if hasattr(d, "get"):
+                if d.has_key(name):
+                    result = d.get(name)
+                else:
+                    result = d.get('DATABASE_' + name)    
+            elif hasattr(d, 'DATABASE_' + name):
+                result = getattr(d, 'DATABASE_' + name)
             else:
-                return getattr(self._dict, name)
+                result = getattr(d, name)
+            return result    
             
     settings = wrap(settings) 
     
-    if settings.DATABASE_NAME == '':
-        raise ImproperlyConfigured("You need to specify a DATABASE_NAME in your Django settings file.")
+    db_name = settings.NAME.strip()
+    db_host = settings.HOST or '127.0.0.1'
+    if len(db_name) == 0:
+        raise ImproperlyConfigured("You need to specify a DATABASE NAME in your Django settings file.")
 
     # Connection strings courtesy of:
     # http://www.connectionstrings.com/?carrier=sqlserver
 
-    datasource = settings.DATABASE_HOST
-    if not datasource:
-        datasource = "127.0.0.1"
-
     # If a port is given, force a TCP/IP connection. The host should be an IP address in this case.
-    if settings.DATABASE_PORT != '':
-        if not _looks_like_ipaddress(datasource):
-            raise ImproperlyConfigured("When using DATABASE_PORT, DATABASE_HOST must be an IP address.")
-        datasource = '%s,%s;Network Library=DBMSSOCN' % (datasource, settings.DATABASE_PORT)
+    if settings.PORT != '':
+        if not _looks_like_ipaddress(db_host):
+            raise ImproperlyConfigured("When using DATABASE PORT, DATABASE HOST must be an IP address.")
+        datasource = '{host},{port};Network Library=DBMSSOCN'.format(
+            host=db_host,
+            port=settings.PORT
+        )
 
     # If no user is specified, use integrated security.
-    if settings.DATABASE_USER != '':
-        auth_string = "UID=%s;PWD=%s" % (settings.DATABASE_USER, settings.DATABASE_PASSWORD)
+    if settings.USER != '':
+        auth_string = "UID={user};PWD={password}".format(
+            user=settings.USER,
+            password=settings.PASSWORD
+        )
     else:
         auth_string = "Integrated Security=SSPI"
 
     parts = [
         "PROVIDER=SQLOLEDB", 
-        "DATA SOURCE="+datasource,
-        "Initial Catalog="+settings.DATABASE_NAME,
+        "DATA SOURCE={0}".format(db_host),
+        "Initial Catalog={0}".format(db_name),
         auth_string
     ]
     
-    options = settings.DATABASE_OPTIONS
+    options = settings.OPTIONS
     if options:
         if 'use_mars' in options and options['use_mars']:
             parts.append("MultipleActiveResultSets=true")
         self.client = BaseDatabaseClient(self)
         self.creation = DatabaseCreation(self) 
         self.introspection = DatabaseIntrospection(self)
-        self.validation = BaseDatabaseValidation()
+        self.validation = BaseDatabaseValidation(self)
 
-        from django.conf import settings
-        self.command_timeout = getattr(settings, 'DATABASE_COMMAND_TIMEOUT', 30)
+        self.command_timeout = getattr(self.settings_dict, 'COMMAND_TIMEOUT', 30)
         if type(self.command_timeout) != int:
             self.command_timeout = 30
         
     def _cursor(self):
         if self.connection is None:
-            
             self.connection = Database.connect(
                                 make_connection_string(self.settings_dict),
                                 self.command_timeout

source/sqlserver_ado/compiler.py

+from django.db.models.sql import compiler
+import re
+
+# query_class returns the base class to use for Django queries.
+# The custom 'SqlServerQuery' class derives from django.db.models.sql.query.Query
+# which is passed in as "QueryClass" by Django itself.
+#
+# SqlServerQuery overrides:
+# ...insert queries to add "SET IDENTITY_INSERT" if needed.
+# ...select queries to emulate LIMIT/OFFSET for sliced queries.
+
+_re_order_limit_offset = re.compile(
+    r'(?:ORDER BY\s+(.+?))?\s*(?:LIMIT\s+(\d+))?\s*(?:OFFSET\s+(\d+))?$')
+
+# Pattern to find the quoted column name at the end of a field specification
+_re_pat_col = re.compile(r"\[([^[]+)\]$")
+
+# Pattern to find each of the parts of a column name (extra_select, table, field)
+_re_pat_col_parts = re.compile(
+    r'(?:' +
+    r'(\([^\)]+\))\s+as\s+' +
+    r'|(\[[^[]+\])\.' +
+    r')?' +
+    r'\[([^[]+)\]$',
+    re.IGNORECASE
+)
+
+# Pattern used in column aliasing to find sub-select placeholders
+_re_col_placeholder = re.compile(r'\{_placeholder_(\d+)\}')
+
+def _break(s, find):
+    """Break a string s into the part before the substring to find, 
+    and the part including and after the substring."""
+    i = s.find(find)
+    return s[:i], s[i:]
+
+def _get_order_limit_offset(sql):
+    return _re_order_limit_offset.search(sql).groups()
+    
+def _remove_order_limit_offset(sql):
+    return _re_order_limit_offset.sub('',sql).split(None, 1)[1]
+
+
+class SQLCompiler(compiler.SQLCompiler):
+    def resolve_columns(self, row, fields=()):
+        # If the results are sliced, the resultset will have an initial 
+        # "row number" column. Remove this column before the ORM sees it.
+        if self._using_row_number:
+            return row[1:]
+        return row
+
+    def as_sql(self, with_limits=True, with_col_aliases=False):
+        self._using_row_number = False
+        
+        # Get out of the way if we're not a select query or there's no limiting involved.
+        check_limits = with_limits and (self.query.low_mark or self.query.high_mark is not None)
+        if not check_limits:
+            return super(SQLCompiler, self).as_sql(with_limits, with_col_aliases)
+
+        raw_sql, fields = super(SQLCompiler, self).as_sql(False, with_col_aliases)
+        
+        # Check for high mark only and replace with "TOP"
+        if self.query.high_mark is not None and not self.query.low_mark:
+            _select = 'SELECT'
+            if self.query.distinct:
+                _select += ' DISTINCT'
+            
+            sql = re.sub(r'(?i)^%s' % _select, '%s TOP %s' % (_select, self.query.high_mark), raw_sql, 1)
+            return sql, fields
+            
+        # Else we have limits; rewrite the query using ROW_NUMBER()
+        self._using_row_number = True
+
+        order, limit_ignore, offset_ignore = _get_order_limit_offset(raw_sql)
+        
+        qn = self.connection.ops.quote_name
+        
+        inner_table_name = qn('AAAA')
+
+        # Using ROW_NUMBER requires an ordering
+        if order is None:
+            meta = self.query.get_meta()                
+            column = meta.pk.db_column or meta.pk.get_attname()
+            order = '%s.%s ASC' % (inner_table_name, qn(column))
+        else:
+            # remap order for injected subselect
+            new_order = []
+            for x in order.split(','):
+                if x.find('.') != -1:
+                    tbl, col = x.rsplit('.', 1)
+                else:
+                    col = x
+                new_order.append('%s.%s' % (inner_table_name, col))
+            order = ', '.join(new_order)
+        
+        where_row_num = "%s < _row_num" % (self.query.low_mark)
+        if self.query.high_mark:
+            where_row_num += " and _row_num <= %s" % (self.query.high_mark)
+            
+        # Lop off ORDER... and the initial "SELECT"
+        inner_select = _remove_order_limit_offset(raw_sql)
+        outer_fields, inner_select = self._alias_columns(inner_select)
+
+        # map a copy of outer_fields for injected subselect
+        f = []
+        for x in outer_fields.split(','):
+            i = x.find(' AS ')
+            if i != -1:
+                x = x[i+4:]
+            if x.find('.') != -1:
+                tbl, col = x.rsplit('.', 1)
+            else:
+                col = x
+            f.append('%s.%s' % (inner_table_name, col.strip()))
+        
+        
+        # inject a subselect to get around OVER requiring ORDER BY to come from FROM
+        inner_select = '%s FROM ( SELECT %s ) AS %s'\
+             % (', '.join(f), inner_select, inner_table_name)
+        
+        sql = "SELECT _row_num, %s FROM ( SELECT ROW_NUMBER() OVER ( ORDER BY %s) as _row_num, %s) as QQQ where %s"\
+             % (outer_fields, order, inner_select, where_row_num)
+        
+        return sql, fields
+
+    def _alias_columns(self, sql):
+        """Return tuple of SELECT and FROM clauses, aliasing duplicate column names."""
+        qn = self.connection.ops.quote_name
+        
+        outer = list()
+        inner = list()
+        names_seen = list()
+        
+        # replace all parens with placeholders
+        paren_depth, paren_buf = 0, ['']
+        parens, i = {}, 0
+        for ch in sql:
+            if ch == '(':
+                i += 1
+                paren_depth += 1
+                paren_buf.append('')
+            elif ch == ')':
+                paren_depth -= 1
+                key = '_placeholder_{0}'.format(i)
+                buf = paren_buf.pop()
+                
+                # store the expanded paren string
+                parens[key] = buf.format(**parens)
+                paren_buf[paren_depth] += '({' + key + '})'
+            else:
+                paren_buf[paren_depth] += ch
+    
+        def _replace_sub(col):
+            """Replace all placeholders with expanded values"""
+            while True:
+                m = _re_col_placeholder.search(col)
+                if m:
+                    try:
+                        key = '_placeholder_{0}'.format(
+                            int(m.group(1))
+                        )
+                        col = col.format(**{
+                            key : parens[key]
+                        })
+                    except:
+                        # not a substituted value
+                        break
+                else:
+                    break
+            return col
+    
+        temp_sql = ''.join(paren_buf)
+    
+        select_list, from_clause = _break(temp_sql, ' FROM [')
+            
+        for col in [x.strip() for x in select_list.split(',')]:
+            match = _re_pat_col.search(col)
+            if match:
+                col_name = match.group(1)
+                col_key = col_name.lower()
+
+                if col_key in names_seen:
+                    alias = qn('%s___%s' % (col_name, names_seen.count(col_key)))
+                    outer.append(alias)
+            
+                    col = _replace_sub(col)
+            
+                    inner.append("%s as %s" % (col, alias))
+                else:
+                    replaced = _replace_sub(col)
+                            
+                    outer.append(qn(col_name))
+                    inner.append(replaced)
+    
+                names_seen.append(col_key)
+            else:
+                raise Exception('Unable to find a column name when parsing SQL: {0}'.format(col))
+
+        return ', '.join(outer), ', '.join(inner) + from_clause.format(**parens)
+
+class SQLInsertCompiler(compiler.SQLInsertCompiler, SQLCompiler):
+    def as_sql(self, *args, **kwargs):
+        sql, params = super(SQLInsertCompiler, self).as_sql(*args, **kwargs)
+
+        meta = self.query.get_meta()
+        
+        if meta.has_auto_field:
+            # db_column is None if not explicitly specified by model field
+            auto_field_column = meta.auto_field.db_column or meta.auto_field.column
+
+            if auto_field_column in self.query.columns:
+                quoted_table = self.connection.ops.quote_name(meta.db_table)
+                sql = "SET IDENTITY_INSERT %s ON;%s;SET IDENTITY_INSERT %s OFF" %\
+                    (quoted_table, sql, quoted_table)
+
+        return sql, params
+
+
+class SQLDeleteCompiler(compiler.SQLDeleteCompiler, SQLCompiler):
+    pass
+
+class SQLUpdateCompiler(compiler.SQLUpdateCompiler, SQLCompiler):
+    pass
+
+class SQLAggregateCompiler(compiler.SQLAggregateCompiler, SQLCompiler):
+    pass
+
+class SQLDateCompiler(compiler.SQLDateCompiler, SQLCompiler):
+    pass

source/sqlserver_ado/dbapi.py

 except ImportError:
     from django.utils import _decimal as decimal
 
+from django.db.utils import IntegrityError as DjangoIntegrityError
+
 import pythoncom
 import win32com.client
 
 class InternalError(DatabaseError): pass
 class OperationalError(DatabaseError): pass
 class ProgrammingError(DatabaseError): pass
-class IntegrityError(DatabaseError): pass
+class IntegrityError(DatabaseError, DjangoIntegrityError): pass
 class DataError(DatabaseError): pass
 class NotSupportedError(DatabaseError): pass
 

source/sqlserver_ado/operations.py

 import datetime
 import time
 
-import query
 
 class DatabaseOperations(BaseDatabaseOperations):
+    compiler_module = "sqlserver_ado.compiler"
+    
     def date_extract_sql(self, lookup_type, field_name):
         return "DATEPART(%s, %s)" % (lookup_type, self.quote_name(field_name))
 
                 replace("]", "\]")
             )
 
-    def query_class(self, DefaultQueryClass):
-        return query.query_class(DefaultQueryClass)
-
     def quote_name(self, name):
         if name.startswith('[') and name.endswith(']'):
             return name # already quoted

source/sqlserver_ado/query.py

-"""Custom Query class for MS SQL Serever."""
-import re
-
-# query_class returns the base class to use for Django queries.
-# The custom 'SqlServerQuery' class derives from django.db.models.sql.query.Query
-# which is passed in as "QueryClass" by Django itself.
-#
-# SqlServerQuery overrides:
-# ...insert queries to add "SET IDENTITY_INSERT" if needed.
-# ...select queries to emulate LIMIT/OFFSET for sliced queries.
-
-_re_order_limit_offset = re.compile(
-    r'(?:ORDER BY\s+(.+?))?\s*(?:LIMIT\s+(\d+))?\s*(?:OFFSET\s+(\d+))?$')
-
-# Pattern to find the quoted column name at the end of a field specification
-_re_pat_col = re.compile(r"\[([^[]+)\]$")
-
-# Pattern to find each of the parts of a column name (extra_select, table, field)
-_re_pat_col_parts = re.compile(
-    r'(?:' +
-    r'(\([^\)]+\))\s+as\s+' +
-    r'|(\[[^[]+\])\.' +
-    r')?' +
-    r'\[([^[]+)\]$',
-    re.IGNORECASE
-)
-
-# Pattern used in column aliasing to find sub-select placeholders
-_re_col_placeholder = re.compile(r'\{_placeholder_(\d+)\}')
-
-def _break(s, find):
-    """Break a string s into the part before the substring to find, 
-    and the part including and after the substring."""
-    i = s.find(find)
-    return s[:i], s[i:]
-
-def _get_order_limit_offset(sql):
-    return _re_order_limit_offset.search(sql).groups()
-    
-def _remove_order_limit_offset(sql):
-    return _re_order_limit_offset.sub('',sql).split(None, 1)[1]
-
-def query_class(QueryClass):
-    """Return a custom Query subclass for SQL Server."""
-    class SqlServerQuery(QueryClass):
-        def __reduce__(self):
-            """
-            Enable pickling for this class (normal pickling handling doesn't
-            work as Python can only pickle module-level classes by default).
-            """
-            if hasattr(QueryClass, '__getstate__'):
-                assert hasattr(QueryClass, '__setstate__')
-                data = self.__getstate__()
-            else:
-                data = self.__dict__
-            return (unpickle_query_class, (QueryClass,), data)
-
-        def __init__(self, *args, **kwargs):
-            super(SqlServerQuery, self).__init__(*args, **kwargs)
-
-            # If we are an insert query, wrap "as_sql"
-            # Import here to avoid circular dependency
-            from django.db.models.sql.subqueries import InsertQuery
-            if isinstance(self, InsertQuery):
-                self._parent_as_sql = self.as_sql
-                self.as_sql = self._insert_as_sql
-
-        def resolve_columns(self, row, fields=()):
-            # If the results are sliced, the resultset will have an initial 
-            # "row number" column. Remove this column before the ORM sees it.
-            if self._using_row_number:
-                return row[1:]
-            return row
-            
-        def as_sql(self, with_limits=True, with_col_aliases=False):
-            self._using_row_number = False
-            
-            # Get out of the way if we're not a select query or there's no limiting involved.
-            check_limits = with_limits and (self.low_mark or self.high_mark is not None)
-            if not isinstance(self, SqlServerQuery) or not check_limits:
-                return super(SqlServerQuery, self).as_sql(with_limits, with_col_aliases)
-
-            raw_sql, fields = super(SqlServerQuery, self).as_sql(False, with_col_aliases)
-            
-            # Check for high mark only and replace with "TOP"
-            if self.high_mark is not None and not self.low_mark:
-                _select = 'SELECT'
-                if self.distinct:
-                    _select += ' DISTINCT'
-                
-                sql = re.sub(r'(?i)^%s' % _select, '%s TOP %s' % (_select, self.high_mark), raw_sql, 1)
-                return sql, fields
-                
-            # Else we have limits; rewrite the query using ROW_NUMBER()
-            self._using_row_number = True
-
-            order, limit_ignore, offset_ignore = _get_order_limit_offset(raw_sql)
-            
-            qn = self.connection.ops.quote_name
-            
-            inner_table_name = qn('AAAA')
-
-            # Using ROW_NUMBER requires an ordering
-            if order is None:
-                meta = self.get_meta()                
-                column = meta.pk.db_column or meta.pk.get_attname()
-                order = '%s.%s ASC' % (inner_table_name, qn(column))
-            else:
-                # remap order for injected subselect
-                new_order = []
-                for x in order.split(','):
-                    if x.find('.') != -1:
-                        tbl, col = x.rsplit('.', 1)
-                    else:
-                        col = x
-                    new_order.append('%s.%s' % (inner_table_name, col))
-                order = ', '.join(new_order)
-            
-            where_row_num = "%s < _row_num" % (self.low_mark)
-            if self.high_mark:
-                where_row_num += " and _row_num <= %s" % (self.high_mark)
-                
-            # Lop off ORDER... and the initial "SELECT"
-            inner_select = _remove_order_limit_offset(raw_sql)
-            outer_fields, inner_select = self._alias_columns(inner_select)
-
-            # map a copy of outer_fields for injected subselect
-            f = []
-            for x in outer_fields.split(','):
-                i = x.find(' AS ')
-                if i != -1:
-                    x = x[i+4:]
-                if x.find('.') != -1:
-                    tbl, col = x.rsplit('.', 1)
-                else:
-                    col = x
-                f.append('%s.%s' % (inner_table_name, col.strip()))
-            
-            
-            # inject a subselect to get around OVER requiring ORDER BY to come from FROM
-            inner_select = '%s FROM ( SELECT %s ) AS %s'\
-                 % (', '.join(f), inner_select, inner_table_name)
-            
-            sql = "SELECT _row_num, %s FROM ( SELECT ROW_NUMBER() OVER ( ORDER BY %s) as _row_num, %s) as QQQ where %s"\
-                 % (outer_fields, order, inner_select, where_row_num)
-            
-            return sql, fields
- 
-        def _alias_columns(self, sql):
-            """Return tuple of SELECT and FROM clauses, aliasing duplicate column names."""
-            qn = self.connection.ops.quote_name
-            
-            outer = list()
-            inner = list()
-            names_seen = list()    
-        
-            # replace all parens with placeholders
-            paren_depth, paren_buf = 0, ['']
-            parens, i = {}, 0
-            for ch in sql:
-                if ch == '(':
-                    i += 1
-                    paren_depth += 1
-                    paren_buf.append('')
-                elif ch == ')':
-                    paren_depth -= 1
-                    key = '_placeholder_{0}'.format(i)
-                    buf = paren_buf.pop()
-                    
-                    # store the expanded paren string
-                    parens[key] = buf.format(**parens)
-                    paren_buf[paren_depth] += '({' + key + '})'
-                else:
-                    paren_buf[paren_depth] += ch
-        
-            def _replace_sub(col):
-                """Replace all placeholders with expanded values"""
-                while True:
-                    m = _re_col_placeholder.search(col)
-                    if m:
-                        try:
-                            key = '_placeholder_{0}'.format(
-                                int(m.group(1))
-                            )
-                            col = col.format(**{
-                                key : parens[key]
-                            })
-                        except:
-                            # not a substituted value
-                            break
-                    else:
-                        break
-                return col
-        
-            temp_sql = ''.join(paren_buf)
-        
-            select_list, from_clause = _break(temp_sql, ' FROM [')
-            
-            for col in [x.strip() for x in select_list.split(',')]:
-                match = _re_pat_col.search(col)
-                if match:
-                    col_name = match.group(1)
-                    col_key = col_name.lower()
-                    
-                    if col_key in names_seen:
-                        alias = qn('%s___%s' % (col_name, names_seen.count(col_key)))
-                        outer.append(alias)
-        
-                        col = _replace_sub(col)
-        
-                        inner.append("%s as %s" % (col, alias))
-                    else:
-                        replaced = _replace_sub(col)
-                        
-                        outer.append(qn(col_name))
-                        inner.append(replaced)
-
-                    names_seen.append(col_key)
-                else:
-                    raise Exception('Unable to find a column name when parsing SQL: {0}'.format(col))
-            
-            return ', '.join(outer), ', '.join(inner) + from_clause.format(**parens)
-
-
-        def _insert_as_sql(self, *args, **kwargs):
-            sql, params = self._parent_as_sql(*args,**kwargs)
-            meta = self.get_meta()
-
-            if meta.has_auto_field:
-                # db_column is None if not explicitly specified by model field
-                auto_field_column = meta.auto_field.db_column or meta.auto_field.column
-
-                if auto_field_column in self.columns:
-                    quoted_table = self.connection.ops.quote_name(meta.db_table)
-                    sql = "SET IDENTITY_INSERT %s ON;%s;SET IDENTITY_INSERT %s OFF" %\
-                        (quoted_table, sql, quoted_table)
-
-            return sql, params
-
-    return SqlServerQuery
-
-def unpickle_query_class(QueryClass):
-    """
-    Utility function, called by Python's unpickling machinery, that handles
-    unpickling of SQL Server Query subclasses.
-    """
-    klass = query_class(QueryClass)
-    return klass.__new__(klass)
-unpickle_query_class.__safe_for_unpickling__ = True

tests/dbsettings.py

 
 import os
 
-DATABASE_ENGINE = 'sqlserver_ado'
-DATABASE_HOST =  os.environ['COMPUTERNAME'] + '\\' + os.environ.get('SQLINSTANCE', 'ss2005')
-DATABASE_PORT = ''
-DATABASE_NAME = r'django_test_backend'
-
-# Use integrated auth.
+# use old style settings for non-django dbapi tests
+DATABASE_NAME = 'django_test_backend'
+DATABASE_HOST = os.environ['COMPUTERNAME'] + '\\' + os.environ.get('SQLINSTANCE', 'ss2008')
 DATABASE_USER = ''
 DATABASE_PASSWORD = ''
+DATABASE_COMMAND_TIMEOUT = 30
+DATABASE_ENGINE = 'sqlserver_ado'
+
+# django required database settings
+DATABASES = {
+    'default': {
+        'NAME': DATABASE_NAME,
+        'ENGINE': 'sqlserver_ado',
+        'HOST': DATABASE_HOST,
+        'USER': DATABASE_USER,
+        'PASSWORD': DATABASE_PASSWORD,
+        'COMMAND_TIMEOUT': DATABASE_COMMAND_TIMEOUT,
+        'OPTIONS' : {
+            #'provider': 'SQLNCLI10',
+            #'extra_params': 'DataTypeCompatibility=80;MARS Connection=True;',
+        },
+    }
+}
 
 # Adds the relative path for the MS SQL Server backend to Python's import path.
 # Note that it pops up two levels because this file is imported from modules another level down,
 def make_connection_string():
     # This function duplicates the Django connection string logic, but is meant
     # to be used by non-Django tests that want to share test db settings.
-    if DATABASE_NAME == '':
+    
+    settings = DATABASES.get('default', {})
+    
+    db_host = settings.get('HOST', '127.0.0.1')
+    db_port = settings.get('PORT', '')
+    db_name = settings.get('NAME', '')
+    db_user = settings.get('USER', '')
+    db_pass = settings.get('PASSWORD', '')
+    
+    if db_name == '':
         raise Exception("You need to specify a DATABASE_NAME in your Django settings file.")
 
-    datasource = DATABASE_HOST
-    if not datasource:
-        datasource = "127.0.0.1"
-
     # If a port is given, force a TCP/IP connection. The host should be an IP address in this case.
-    if DATABASE_PORT != '':
-        if not _looks_like_ipaddress(datasource):
+    if db_port != '':
+        if not _looks_like_ipaddress(db_host):
             raise Exception("When using DATABASE_PORT, DATABASE_HOST must be an IP address.")
-        datasource = '%s,%s;Network Library=DBMSSOCN' % (datasource, DATABASE_PORT)
+        datasource = '%s,%s;Network Library=DBMSSOCN' % (db_host, db_port)
 
     # If no user is specified, use integrated security.
-    if DATABASE_USER != '':
-        auth_string = "UID=%s;PWD=%s" % (DATABASE_USER, DATABASE_PASSWORD)
+    if db_user != '':
+        auth_string = "UID=%s;PWD=%s" % (db_user, db_pass)
     else:
         auth_string = "Integrated Security=SSPI"
 
     return "PROVIDER=SQLOLEDB;DATA SOURCE=%s;Initial Catalog=%s;%s" % \
-        (datasource, DATABASE_NAME, auth_string)
+        (db_host, db_name, auth_string)

tests/test_main/regressiontests/models.py

     related_obj = models.ForeignKey(Bug69Table1, db_column='Table1Id')
     
 
-class MyAutoField(models.AutoField): pass
+class MyAutoField(models.AutoField): 
+    pass
 
 class Bug70Table(models.Model):
 	"""
     >>> len(list(Bug70Table.objects.all()))
     3
 	"""
-	id = models.MyAutoField(primary_key=True, db_column="Table70Id")
+	id = MyAutoField(primary_key=True, db_column="Table70Id")
 	a = models.IntegerField()