jayd3e avatar jayd3e committed c60e590

Autogenerate now orders tables correctly. Dependency tables now appear before their dependant tables. Also added the respective tests.

Comments (0)

Files changed (2)

alembic/autogenerate.py

 
 from alembic import util
 from sqlalchemy.engine.reflection import Inspector
+from sqlalchemy.util._collections import OrderedSet
 from sqlalchemy import schema, types as sqltypes
 import re
 
     # TODO: not hardcode alembic_version here ?
     conn_table_names = set(inspector.get_table_names()).\
                             difference(['alembic_version'])
-    metadata_table_names = set(metadata.tables)
+    metadata_table_names = OrderedSet([table.name for table in metadata.sorted_tables])
 
-    _compare_tables(conn_table_names, metadata_table_names, inspector, metadata, diffs, autogen_context)
+    _compare_tables(conn_table_names, metadata_table_names,
+                    inspector, metadata, diffs, autogen_context)
 
 def _compare_tables(conn_table_names, metadata_table_names, 
                     inspector, metadata, diffs, autogen_context):
 
 def _produce_downgrade_commands(diffs, autogen_context):
     buf = []
-    for diff in diffs:
+    for diff in reversed(diffs):
         buf.append(_invoke_command("downgrade", diff, autogen_context))
     if not buf:
         buf = ["pass"]

tests/test_autogenerate.py

     )
     return m
 
+def _model_three():
+    m = MetaData()
+    return m
+
+def _model_four():
+    m = MetaData()
+
+    Table('parent', m,
+        Column('id', Integer, primary_key=True)
+    )
+
+    Table('child', m,
+        Column('parent_id', Integer, ForeignKey('parent.id')),
+    )
+
+    return m
+
 class AutogenerateDiffTest(TestCase):
     @classmethod
     @requires_07
         connection = self.context.bind
         diffs = []
         autogenerate._produce_net_changes(connection, metadata, diffs, 
-                                        self.autogen_context)
-
+                                          self.autogen_context)
+        
         eq_(
             diffs[0],
             ('add_table', metadata.tables['item'])
         eq_(diffs[7][0][4], True)
         eq_(diffs[7][0][5], False)
 
-
     def test_render_nothing(self):
         context = MigrationContext.configure(
             connection = self.bind.connect(),
     ### end Alembic commands ###""")
         eq_(re.sub(r"u'", "'", template_args['downgrades']),
 """### commands auto generated by Alembic - please adjust! ###
-    op.drop_table('item')
+    op.alter_column('user', 'name', 
+               existing_type=sa.VARCHAR(length=50), 
+               nullable=True)
+    op.alter_column('user', 'a1', 
+               existing_type=sa.TEXT(), 
+               server_default=None, 
+               existing_nullable=True)
+    op.add_column('user', sa.Column('pw', sa.VARCHAR(length=50), nullable=True))
+    op.alter_column('order', 'amount', 
+               existing_type=sa.Numeric(precision=10, scale=2), 
+               type_=sa.NUMERIC(precision=8, scale=2), 
+               nullable=False, 
+               existing_server_default='0')
+    op.drop_column('order', 'user_id')
+    op.drop_column('address', 'street')
     op.create_table('extra',
     sa.Column('x', sa.CHAR(), nullable=True),
     sa.Column('uid', sa.INTEGER(), nullable=True),
     sa.ForeignKeyConstraint(['uid'], ['user.id'], ),
     sa.PrimaryKeyConstraint()
     )
-    op.drop_column('address', 'street')
-    op.drop_column('order', 'user_id')
-    op.alter_column('order', 'amount', 
-               existing_type=sa.Numeric(precision=10, scale=2), 
-               type_=sa.NUMERIC(precision=8, scale=2), 
-               nullable=False, 
-               existing_server_default='0')
-    op.add_column('user', sa.Column('pw', sa.VARCHAR(length=50), nullable=True))
-    op.alter_column('user', 'a1', 
-               existing_type=sa.TEXT(), 
-               server_default=None, 
-               existing_nullable=True)
-    op.alter_column('user', 'name', 
-               existing_type=sa.VARCHAR(length=50), 
-               nullable=True)
+    op.drop_table('item')
     ### end Alembic commands ###""")
 
     def test_skip_null_type_comparison_reflected(self):
             [('remove_table', 'extra'), ('remove_table', u'user')]
         )
 
+class AutogenerateDiffOrderTest(TestCase):
+    @classmethod
+    @requires_07
+    def setup_class(cls):
+        staging_env()
+        cls.bind = sqlite_db()
+        cls.m3 = _model_three()
+        cls.m3.create_all(cls.bind)
+        cls.m4 = _model_four()
+
+        cls.empty_context = empty_context = MigrationContext.configure(
+            connection = cls.bind.connect(),
+            opts = {
+                'compare_type':True,
+                'compare_server_default':True,
+                'target_metadata':cls.m3,
+                'upgrade_token':"upgrades",
+                'downgrade_token':"downgrades",
+                'alembic_module_prefix':'op.',
+                'sqlalchemy_module_prefix':'sa.'
+            }
+        )
+        
+        connection = empty_context.bind
+        cls.autogen_empty_context = {
+            'imports':set(),
+            'connection':connection,
+            'dialect':connection.dialect,
+            'context':empty_context
+            }
+
+    @classmethod
+    def teardown_class(cls):
+        clear_staging_env()
+    
+    def test_diffs_order(self):
+        """
+        Added in order to test that child tables(tables with FKs) are generated
+        before their parent tables
+        """
+
+        metadata = self.m4
+        connection = self.empty_context.bind
+        diffs = []
+        
+        autogenerate._produce_net_changes(connection, metadata, diffs, 
+                                          self.autogen_empty_context)
+        
+        eq_(diffs[0][0], 'add_table')
+        eq_(diffs[0][1].name, "parent")
+        eq_(diffs[1][0], 'add_table')
+        eq_(diffs[1][1].name, "child")
+
 class AutogenRenderTest(TestCase):
     """test individual directives"""
 
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.