]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
added 'checkfirst' argument to table.create()/table.drop()
authorMike Bayer <mike_mp@zzzcomputing.com>
Wed, 19 Jul 2006 22:13:29 +0000 (22:13 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Wed, 19 Jul 2006 22:13:29 +0000 (22:13 +0000)
some 0.2.6 prep

CHANGES
doc/build/content/document_base.myt
doc/build/content/metadata.txt
lib/sqlalchemy/ansisql.py
lib/sqlalchemy/engine/base.py
lib/sqlalchemy/schema.py
setup.py
test/engine/reflection.py

diff --git a/CHANGES b/CHANGES
index 86a65d7284bf1a0d724526c5d55cf7d13a8bc2ff..564eef21fd65e421dd55473b9af94e67f9dddb66 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -44,6 +44,7 @@ aware of their "inherited" status if so.
 relationships to an inheriting mapper (which is also self-referential)
 - reduced bind param size in query._get to appease the picky oracle 
 [ticket:244]
+- added 'checkfirst' argument to table.create()/table.drop()
 
 0.2.5
 - fixed endless loop bug in select_by(), if the traversal hit
index ceb596b1cf0caa1cc5c732750e0acb48d7ebc354..f1cb3241f7fda93ad068da3dffd6013d90b434c9 100644 (file)
@@ -24,7 +24,7 @@
     onepage='documentation'
     index='index'
     title='SQLAlchemy 0.2 Documentation'
-    version = '0.2.5'
+    version = '0.2.6'
 </%attr>
 
 <%method title>
index 48941347a90d40744a7ca301db9d25da9ffee040..0bce1f5e81b76c80d440bc010885613e2e3d0255 100644 (file)
@@ -31,6 +31,27 @@ Then to construct a Table, use the `Table` class:
 
 The specific datatypes for each Column, such as Integer, String, etc. are described in [types](rel:types), and exist within the module `sqlalchemy.types` as well as the global `sqlalchemy` namespace.
 
+Foreign keys are most easily specified by the `ForeignKey` object within a `Column` object.  For a composite foreign key, i.e. a foreign key that contains multiple columns referencing multiple columns to a composite primary key, an explicit syntax is provided which allows the correct table CREATE statements to be generated:
+
+    {python}
+    # a table with a composite primary key
+    invoices = Table('invoices', metadata, 
+        Column('invoice_id', Integer, primary_key=True),
+        Column('ref_num', Integer, primary_key=True),
+        Column('description', String(60), nullable=False)
+    )
+    
+    # a table with a composite foreign key referencing the parent table
+    invoice_items = Table('invoice_items', metadata, 
+        Column('item_id', Integer, primary_key=True),
+        Column('item_name', String(60), nullable=False),
+        Column('invoice_id', Integer, nullable=False),
+        Column('ref_num', Integer, nullable=False),
+        ForeignKeyConstraint(['invoice_id', 'ref_num'], ['invoices.invoice_id', 'invoices.ref_num'])
+    )
+    
+Above, the `invoice_items` table will have `ForeignKey` objects automatically added to the `invoice_id` and `ref_num` `Column` objects as a result of the additional `ForeignKeyConstraint` object.
+
 The `MetaData` object supports some handy methods, such as getting a list of Tables in the order (or reverse) of their dependency:
 
     {python}
@@ -238,7 +259,13 @@ Creating and dropping individual tables can be done via the `create()` and `drop
     DROP TABLE employees
     {}            
 
-Entire groups of Tables can be created and dropped directly from the `MetaData` object with `create_all()` and `drop_all()`, each of which take an optional `engine` keyword argument which can reference an `Engine` or a `Connection`, else the underlying bound `Engine` is used:
+The `create()` and `drop()` methods also support an optional keyword argument `checkfirst` which will issue the database's appropriate pragma statements to check if the table exists before creating or dropping:
+
+    {python}
+    employees.create(engine=e, checkfirst=True)
+    employees.drop(checkfirst=False)
+    
+Entire groups of Tables can be created and dropped directly from the `MetaData` object with `create_all()` and `drop_all()`.  These methods always check for the existence of each table before creating or dropping.  Each method takes an optional `engine` keyword argument which can reference an `Engine` or a `Connection`.  If no engine is specified, the underlying bound `Engine`,  if any, is used:
 
     {python}
     engine = create_engine('sqlite:///:memory:')
index 02423560ba521d746f46fb38563521850fbedd9b..3ba2965769280d922e440f1c7cb94fb5eb8a3b94 100644 (file)
@@ -602,6 +602,10 @@ class ANSICompiler(sql.Compiled):
 
 
 class ANSISchemaGenerator(engine.SchemaIterator):
+    def __init__(self, engine, proxy, connection=None, checkfirst=False, **params):
+        super(ANSISchemaGenerator, self).__init__(engine, proxy, **params)
+        self.checkfirst = checkfirst
+        self.connection = connection
     def get_column_specification(self, column, first_pk=False):
         raise NotImplementedError()
         
@@ -609,6 +613,9 @@ class ANSISchemaGenerator(engine.SchemaIterator):
         # the single whitespace before the "(" is significant
         # as its MySQL's method of indicating a table name and not a reserved word.
         # feel free to localize this logic to the mysql module
+        if self.checkfirst and self.engine.dialect.has_table(self.connection, table.name):
+            return
+            
         self.append("\nCREATE TABLE " + table.fullname + " (")
         
         separator = "\n"
@@ -682,6 +689,11 @@ class ANSISchemaGenerator(engine.SchemaIterator):
         
     
 class ANSISchemaDropper(engine.SchemaIterator):
+    def __init__(self, engine, proxy, connection=None, checkfirst=False, **params):
+        super(ANSISchemaDropper, self).__init__(engine, proxy, **params)
+        self.checkfirst = checkfirst
+        self.connection = connection
+
     def visit_index(self, index):
         self.append("\nDROP INDEX " + index.name)
         self.execute()
@@ -689,6 +701,8 @@ class ANSISchemaDropper(engine.SchemaIterator):
     def visit_table(self, table):
         # NOTE: indexes on the table will be automatically dropped, so
         # no need to drop them individually
+        if self.checkfirst and not self.engine.dialect.has_table(self.connection, table.name):
+            return
         self.append("\nDROP TABLE " + table.fullname)
         self.execute()
 
index 048c2400988748dcbc76d7ede6504bba1b8d6c51..ea678152e3ed9d7816f5040bb07c98a1bf09b509 100644 (file)
@@ -420,7 +420,7 @@ class ComposedSQLEngine(sql.Engine, Connectable):
         else:
             conn = connection
         try:
-            element.accept_schema_visitor(visitorcallable(self, conn.proxy, **kwargs))
+            element.accept_schema_visitor(visitorcallable(self, conn.proxy, connection=conn, **kwargs))
         finally:
             if connection is None:
                 conn.close()
index 78dbe38b37f14ac440b281d8f424be6d62fd4ab7..8fce1665d86ae8488a36821a4789f3612cc61661 100644 (file)
@@ -236,17 +236,17 @@ class Table(SchemaItem, sql.TableClause):
         this does not issue a SQL DROP statement."""
         key = _get_table_key(self.name, self.schema)
         del self.metadata.tables[key]
-    def create(self, connectable=None):
+    def create(self, connectable=None, checkfirst=False):
         if connectable is not None:
-            connectable.create(self)
+            connectable.create(self, checkfirst=checkfirst)
         else:
-            self.engine.create(self)
+            self.engine.create(self, checkfirst=checkfirst)
         return self
-    def drop(self, connectable=None):
+    def drop(self, connectable=None, checkfirst=False):
         if connectable is not None:
-            connectable.drop(self)
+            connectable.drop(self, checkfirst=checkfirst)
         else:
-            self.engine.drop(self)
+            self.engine.drop(self, checkfirst=checkfirst)
     def tometadata(self, metadata, schema=None):
         """return a copy of this Table associated with a different MetaData."""
         try:
index b6f78f08f2347bc9b89617308726904cf7b5f344..863bf1e33d27904a67c008aa7bb90ff677f6e574 100644 (file)
--- a/setup.py
+++ b/setup.py
@@ -3,7 +3,7 @@ use_setuptools()
 from setuptools import setup, find_packages
 
 setup(name = "SQLAlchemy",
-    version = "0.2.5",
+    version = "0.2.6",
     description = "Database Abstraction Library",
     author = "Mike Bayer",
     author_email = "mike_mp@zzzcomputing.com",
index ec59d652cdd947e74e2973a11496e6bf932089d7..64a645d0f967328ad15a2c2713aa772785280439 100644 (file)
@@ -155,6 +155,22 @@ class ReflectionTest(PersistTest):
         finally:
             meta.drop_all()
 
+    def testcheckfirst(self):
+        meta = BoundMetaData(testbase.db)
+        
+        table = Table('checkfirst', meta, 
+            Column('col1', Integer, primary_key=True),
+            Column('col2', String(40)))
+        try:
+            table.create()
+            table.create(checkfirst=True)
+            table.drop()
+            table.drop(checkfirst=True)
+            table.create(checkfirst=True)
+            table.drop()
+        finally:
+            meta.drop_all()
+            
     def testtoengine(self):
         meta = MetaData('md1')
         meta2 = MetaData('md2')