Commits

Andrew Godwin committed fbc8b18 Merge

Comments (0)

Files changed (4)

south/db/generic.py

     allows_combined_alters = True
     supports_foreign_keys = True
     has_check_constraints = True
+    has_booleans = True
 
     @cached_property
     def has_ddl_transactions(self):
         except ImportError:
             pass
         # Now do the test
-        if connection.features.supports_transactions:
+        if getattr(connection.features, 'supports_transactions', True):
             cursor = connection.cursor()
             self.start_transaction()
             cursor.execute('CREATE TABLE DDL_TRANSACTION_TEST (X INT)')
         except DatabaseError, e:
             print >> sys.stderr, 'FATAL ERROR - The following SQL query failed: %s' % sql
             print >> sys.stderr, 'The error was: %s' % e
-            sys.exit(1)
+            raise
 
         try:
             return cursor.fetchall()
                             default = default()
                             
                         default = field.get_db_prep_save(default, connection=self._get_connection())
+                        default = self._default_value_workaround(default)
                         # Now do some very cheap quoting. TODO: Redesign return values to avoid this.
                         if isinstance(default, basestring):
                             default = "'%s'" % default.replace("'", "''")
         """
         return field
 
+    def _default_value_workaround(self, value):
+        """
+        DBMS-specific value alterations (this really works around
+        missing functionality in Django backends)
+        """
+        if isinstance(value, bool) and not self.has_booleans:
+            return int(value)
+        else:
+            return value 
 
     def foreign_key_sql(self, from_table_name, from_column_name, to_table_name, to_column_name):
         """

south/db/oracle.py

     add_constraint_string =     'ALTER TABLE %(table_name)s ADD CONSTRAINT %(constraint)s %(clause)s'
 
     allows_combined_alters = False
+    has_booleans = False
     
     constraints_dict = {
         'P': 'PRIMARY KEY',
         else:
             return original_get_sequence_name(table_name)
 
+    #TODO: This will cause very obscure bugs if anyone uses a column name or string value
+    #      that looks like a column definition (with 'CHECK', 'DEFAULT' and/or 'NULL' in it)
+    #      e.g. "CHECK MATE" varchar(10) DEFAULT 'NULL'
     def adj_column_sql(self, col):
-        # Fix boolean field values: need to be 1/0, not True/False
-        col = re.sub('DEFAULT True', 'DEFAULT 1', col)
-        col = re.sub('DEFAULT False', 'DEFAULT 0', col)
-        # Fix other things
+        # Syntax fixes -- Oracle is picky about clause order
         col = re.sub('(?P<constr>CHECK \(.*\))(?P<any>.*)(?P<default>DEFAULT \d+)', 
                      lambda mo: '%s %s%s'%(mo.group('default'), mo.group('constr'), mo.group('any')), col) #syntax fix for boolean/integer field only
         col = re.sub('(?P<not_null>(NOT )?NULL) (?P<misc>(.* )?)(?P<default>DEFAULT.+)',
 
     @generic.invalidate_table_constraints
     def alter_column(self, table_name, name, field, explicit_name=True):
+        
+        if self.dry_run:
+            if self.debug:
+                print '   - no dry run output for alter_column() due to dynamic DDL, sorry'
+            return
+
         qn = self.quote_name(table_name)
 
         # hook for the field to do any resolution prior to it's attributes being queried
             params['nullity'] = 'NULL'
 
         if not field.null and field.has_default():
-            params['default'] = field.get_default()
+            params['default'] = self._default_value_workaround(field.get_default())
 
         sql_templates = [
             (self.alter_string_set_type, params),
             field.default = int(field.to_python(field.get_default()))
         return field
 
+    def _default_value_workaround(self, value):
+        from datetime import date,time,datetime
+        if isinstance(value, (date,time,datetime)):
+            return "'%s'" % value
+        else:
+            return super(DatabaseOperations, self)._default_value_workaround(value)
+
     def _fill_constraint_cache(self, db_name, table_name):
         self._constraint_cache.setdefault(db_name, {}) 
         self._constraint_cache[db_name][table_name] = {} 

south/db/sql_server/pyodbc.py

     
     
     default_schema_name = "dbo"
+    
+    has_booleans = False
 
 
     @delete_column_constraints
         else:
             #TODO: Anybody else needs special translations?
             return str(value) 
-
+    def _default_value_workaround(self, value):
+        if isinstance(value, (date,time,datetime)):
+            return value.isoformat()
+        else:
+            return super(DatabaseOperations, self)._default_value_workaround(value)
+        
     def _quote_string(self, s):
         return "'" + s.replace("'","''") + "'"
     

south/tests/db.py

         db.create_table("test_textdef", [
             ('textcol', models.TextField(blank=True)),
         ])
-    
+
+    def test_datetime_default(self):
+        """
+        Test that defaults are created correctly for datetime columns
+        """    
+        from datetime import datetime
+
+        end_of_world = datetime(2012, 12, 21, 0, 0, 1)
+  
+        db.create_table("test_datetime_def", [
+            ('col0', models.IntegerField(null=True)),
+            ('col1', models.DateTimeField(default=end_of_world)),
+            ('col2', models.DateTimeField(null=True)),
+        ])
+        db.alter_column("test_datetime_def", "col2", models.DateTimeField(default=end_of_world))        
+        db.add_column("test_datetime_def", "col3", models.DateTimeField(default=end_of_world))
+        db.execute("insert into test_datetime_def (col0) values (null)")
+        ends = db.execute("select col1,col2,col3 from test_datetime_def")[0]
+        self.failUnlessEqual(len(ends), 3)
+        for e in ends:
+            self.failUnlessEqual(e, end_of_world)
+        
     def test_add_unique_fk(self):
         """
         Test adding a ForeignKey with unique=True or a OneToOneField
                     return value
                 return map(int, value.split(','))
 
+        false_value = db.has_booleans and 'False' or '0' 
         defaults = (
-            (models.DateTimeField(default=datetime(2012, 12, 21, 0, 0, 1)), 'DEFAULT \'2012-12-21 00:00:01'),
+            #(models.DateTimeField(default=datetime(2012, 12, 21, 0, 0, 1)), 'DEFAULT \'2012-12-21 00:00:01'), # replaced by test_datetime_default
             (models.CharField(default='sukasuka'), 'DEFAULT \'sukasuka'),
-            (models.BooleanField(default=False), 'DEFAULT False'),
+            (models.BooleanField(default=False), 'DEFAULT %s' % false_value),
             (models.IntegerField(default=42), 'DEFAULT 42'),
             (CustomField(default=[2012,2018,2021,2036]), 'DEFAULT \'2012,2018,2021,2036')
         )
         for field, sql_test_str in defaults:
             sql = db.column_sql('fish', 'YAAAAAAZ', field)
             if sql_test_str not in sql:
-                self.fail("default sql value was not properly generated for field %r." % field)
+                self.fail("default sql value was not properly generated for field %r.\nSql was %s" % (field,sql))   
 
 
         
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.