Commits

Stefan Zimmermann committed 22f2bae Merge

merge with south default

  • Participants
  • Parent commits a1b53ab, eabd02a

Comments (0)

Files changed (16)

 7e4cbf82243f359651c5c14dca85cb331a282e3f stableish
 b35fdff583d19e534a648cb9a5fd03e866b6a475 0.7.3
 738417d7a8abaa144a56fc8f42c1fe01b79eb9f6 0.7.4
+3f6f2066c47c8ae26e81277f4efe08e2d4b28f8d 0.7.5
 
 --- Original README: ---
 
-This is South, a Django application to provide migrations in a sane way.
-
-By sane, we mean that the status of every migration is tracked individually,
-rather than just the number of the top migration reached; this means South
-can detect when you have an unapplied migration that's sitting in the middle
-of a whole load of applied ones, and will let you apply it straight off,
-or let you roll back to it, and apply from there forward.
+This is South, a Django application to provide schema and data migrations.
 
 Documentation on South is currently available on our project site;
 you can find it at http://south.aeracode.org/docs/
+
+South is compatable with Django 1.2 and higher, and Python 2.4 and higher.

File docs/index.rst

 South is a tool to provide consistent, easy-to-use and database-agnostic
 migrations for Django applications.
 
-This is the documentation for the current version (0.7); previous versions'
+This is the documentation for the current version series (0.7.x); previous versions'
 documentation was written directly into our wiki, but is mostly a subset of
 what is written here.
 
    signals
    fixtures
    settings
-   releasenotes/index
+   releasenotes/index

File docs/installation.rst

 Installation
 ============
 
-South's current release is :ref:`0.7.4 <0-7-4-release-notes>`.
+South's current release is :ref:`0.7.5 <0-7-5-release-notes>`.
 
 There are a few different ways to install South:
 

File docs/releasenotes/0.7.5.rst

+
+.. _0-7-5-release-notes:
+
+===========
+South 0.7.5
+===========
+
+This is a minor new release of South, and the fifth bugfix release for the 
+:ref:`0.7 series <0-7-release-notes>`.
+
+
+Compatability Notes
+===================
+
+From now on, South will only officially be compatable with Django 1.2 and up, and thus only with Python 2.4 and up. Future versions of South are likely to require newer and newer versions of Django in order to simplify the codebase somewhat.
+
+
+Backwards incompatible changes
+==============================
+
+None.
+
+
+Index naming
+============
+
+Single-column indexes should now be named the same as if they were created using ``syncdb``
+
+
+UUIDs
+=====
+
+UUID default values now work correctly on the PostgreSQL backend.
+
+
+Transactions
+============
+
+Transactions now use the correct database in a multi-db setup - previously, they were sometimes using the default database instead of another configured one.
+
+
+Recursive Foreign Keys
+======================
+
+Deletion of self-referencing ForeignKeys is now possible again.
+
+
+Unmanaged models
+================
+
+A bug with ignoring changes to unmanaged models has been fixed, so they are now ignored properly.
+
+
+Oracle/SQL Server
+=================
+
+A few minor fixes, including the ability to change TextFields to CharFields and vice-versa on the Oracle backend.
+
+
+Other fixes
+===========
+
+As usual, there are tens of other minor fixes throughout the codebase. Full details of those are available on Trac.
         'south.tests.otherfakeapp',
         'south.tests.deps_c',
         'south.tests.deps_b',
+        'south.tests.non_managed',
         'south.tests.circular_a.migrations',
         'south.tests.emptyapp.migrations',
         'south.tests.deps_a.migrations',
         'south.tests.otherfakeapp.migrations',
         'south.tests.deps_c.migrations',
         'south.tests.deps_b.migrations',
+        'south.tests.non_managed.migrations',
         'south.utils',
     ],
 )

File south/__init__.py

 South - Useable migrations for Django apps
 """
 
-__version__ = "0.7.4"
+__version__ = "0.7.5"
 __authors__ = [
     "Andrew Godwin <andrew@aeracode.org>",
     "Andy McCurdy <andy@andymccurdy.com>"

File south/creator/changes.py

                 old_fields, old_meta, old_m2ms = self.split_model_def(self.old_orm[key], self.old_defs[key])
                 new_fields, new_meta, new_m2ms = self.split_model_def(self.current_model_from_key(key), self.new_defs[key])
                 
+                # Do nothing for models which are now not managed.
+                if new_meta.get("managed", "True") == "False":
+                    continue
+                
                 # Find fields that have vanished.
                 for fieldname in old_fields:
                     if fieldname not in new_fields:

File south/db/generic.py

             cursor.execute('CREATE TABLE DDL_TRANSACTION_TEST (X INT)')
             self.rollback_transaction()
             try:
-                cursor.execute('CREATE TABLE DDL_TRANSACTION_TEST (X INT)')
-            except exceptions:
-                return False
-            else:
-                return True
+                try:
+                    cursor.execute('CREATE TABLE DDL_TRANSACTION_TEST (X INT)')
+                except exceptions:
+                    return False
+                else:
+                    return True
             finally:
                 cursor.execute('DROP TABLE DDL_TRANSACTION_TEST')
         else:
         """
 
         # If there is just one column in the index, use a default algorithm from Django
-        if len(column_names) == 1:
+        if len(column_names) == 1 and not suffix:
             return truncate_name(
                 '%s_%s' % (table_name, self._digest(column_names[0])),
                 self._get_connection().ops.max_name_length()

File south/db/sqlite3.py

             type = column_info['type'].replace("PRIMARY KEY", "")
             # Add on primary key, not null or unique if needed.
             if (primary_key_override and primary_key_override == name) or \
-               (not primary_key_override and indexes[name]['primary_key']):
+               (not primary_key_override and name in indexes and
+                indexes[name]['primary_key']):
                 type += " PRIMARY KEY"
             elif not column_info['null_ok']:
                 type += " NOT NULL"
-            if indexes[name]['unique'] and name not in uniques_deleted:
+            if (name in indexes and indexes[name]['unique'] and
+                name not in uniques_deleted):
                 type += " UNIQUE"
             if column_info['dflt_value'] is not None:
                 type += " DEFAULT " + column_info['dflt_value']
         # Add on altered columns
         for name, type in altered.items():
             if (primary_key_override and primary_key_override == name) or \
-               (not primary_key_override and indexes[name]['primary_key']):
+               (not primary_key_override and name in indexes and
+                indexes[name]['primary_key']):
                 type += " PRIMARY KEY"
-            if indexes[name]['unique'] and name not in uniques_deleted:
+            if (name in indexes and indexes[name]['unique'] and
+                name not in uniques_deleted):
                 type += " UNIQUE"
             definitions[name] = type
         # Add on the new columns

File south/tests/__init__.py

File contents unchanged.

File south/tests/autodetection.py

 import unittest
 
-from south.creator.changes import AutoChanges
+from south.creator.changes import AutoChanges, InitialChanges
+from south.migration.base import Migrations
+from south.tests import Monkeypatcher
+from south.creator import freezer
+from south.orm import FakeORM
+from south.v2 import SchemaMigration
 
 class TestComparison(unittest.TestCase):
     
                 ('django.db.models.fields.IntField', [], {'to':'hah'}),
             ),
             True,
-        )
+        )
+
+class TestNonManagedIgnored(Monkeypatcher):
+    
+    installed_apps = ["non_managed"]
+
+    full_defs = {
+        'non_managed.legacy': {
+            'Meta': {'object_name': 'Legacy', 'db_table': "'legacy_table'", 'managed': 'False'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True'}),
+            'size': ('django.db.models.fields.IntegerField', [], {})
+        }
+    } 
+
+    def test_not_added_init(self):
+        
+        migrations = Migrations("non_managed")
+        changes = InitialChanges(migrations)
+        change_list = changes.get_changes()
+        if list(change_list):
+            self.fail("Initial migration creates table for non-managed model")
+
+    def test_not_added_auto(self):
+
+        empty_defs = { }
+        class EmptyMigration(SchemaMigration):
+            "Serves as fake previous migration"
+        
+            def forwards(self, orm):
+                pass
+        
+            def backwards(self, orm):
+                pass
+        
+            models = empty_defs
+
+            complete_apps = ['non_managed']
+                    
+        migrations = Migrations("non_managed")
+        empty_orm = FakeORM(EmptyMigration, "non_managed")
+        changes = AutoChanges(
+            migrations = migrations,
+            old_defs = empty_defs,
+            old_orm = empty_orm,
+            new_defs = self.full_defs,
+        )
+        change_list = changes.get_changes()
+        if list(change_list):
+            self.fail("Auto migration creates table for non-managed model")
+
+    def test_not_deleted_auto(self):
+
+        empty_defs = { }
+        old_defs = freezer.freeze_apps(["non_managed"])
+        class InitialMigration(SchemaMigration):
+            "Serves as fake previous migration"
+        
+            def forwards(self, orm):
+                pass
+        
+            def backwards(self, orm):
+                pass
+        
+            models = self.full_defs
+
+            complete_apps = ['non_managed']
+                    
+        migrations = Migrations("non_managed")
+        initial_orm = FakeORM(InitialMigration, "non_managed")
+        changes = AutoChanges(
+            migrations = migrations,
+            old_defs = self.full_defs,
+            old_orm = initial_orm,
+            new_defs = empty_defs,
+        )
+        change_list = changes.get_changes()
+        if list(change_list):
+            self.fail("Auto migration deletes table for non-managed model")
+
+    def test_not_modified_auto(self):
+
+        fake_defs = {
+            'non_managed.legacy': {
+                'Meta': {'object_name': 'Legacy', 'db_table': "'legacy_table'", 'managed': 'False'},
+                'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+                'name': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True'}),
+                #'size': ('django.db.models.fields.IntegerField', [], {}) # The "change" is the addition of this field
+            }
+        } 
+        class InitialMigration(SchemaMigration):
+            "Serves as fake previous migration"
+        
+            def forwards(self, orm):
+                pass
+        
+            def backwards(self, orm):
+                pass
+        
+            models = fake_defs
+
+            complete_apps = ['non_managed']
+                    
+        from non_managed import models as dummy_import_to_force_loading_models # TODO: Does needing this indicate a bug in MokeyPatcher? 
+        
+        migrations = Migrations("non_managed")
+        initial_orm = FakeORM(InitialMigration, "non_managed")
+        changes = AutoChanges(
+            migrations = migrations,
+            old_defs = fake_defs,
+            old_orm = initial_orm,
+            new_defs = self.full_defs
+        )
+        change_list = changes.get_changes()
+        if list(change_list):
+            self.fail("Auto migration changes table for non-managed model")

File south/tests/inspector.py

-from south.tests import Monkeypatcher
-from south.modelsinspector import *
+
+from south.tests import Monkeypatcher, skipUnless
+from south.modelsinspector import (convert_on_delete_handler, get_value,
+    IsDefault, models, value_clean)
+
 from .fakeapp.models import HorribleModel, get_sentinel_object
 
-from django.utils.functional import wraps
 
 on_delete_is_available = hasattr(models, "PROTECT") # models here is django.db.models
-
-from south.tests import skipUnless        
 skipUnlessOnDeleteAvailable = skipUnless(on_delete_is_available, "not testing on_delete -- not available on Django<1.3")                    
 
-
 class TestModelInspector(Monkeypatcher):
 
     """

File south/tests/non_managed/__init__.py

Empty file added.

File south/tests/non_managed/migrations/__init__.py

Empty file added.

File south/tests/non_managed/models.py

+# -*- coding: UTF-8 -*-
+
+"""
+An app with a model that is not managed for testing that South does
+not try to manage it in any way
+"""
+from django.db import models
+
+class Legacy(models.Model):
+    
+    name = models.CharField(max_length=10)
+    size = models.IntegerField()
+    
+    class Meta:
+        db_table = "legacy_table"
+        managed = False