Database Meta Data
Version: 0.1.0 Last Updated: 02/13/06 21:52:15
View: Paged  |  One Page
Describing Tables with MetaData

The core of SQLAlchemy's query and object mapping operations is table metadata, which are Python objects that describe tables. Metadata objects can be created by explicitly naming the table and all its properties, using the Table, Column, ForeignKey, and Sequence objects imported from sqlalchemy.schema, and a database engine constructed as described in the previous section, or they can be automatically pulled from an existing database schema. First, the explicit version:

from sqlalchemy import *
engine = create_engine('sqlite', {'filename':':memory:'}, **opts)

users = Table('users', engine, 
    Column('user_id', Integer, primary_key = True),
    Column('user_name', String(16), nullable = False),
    Column('email_address', String(60), key='email'),
    Column('password', String(20), nullable = False)
)

user_prefs = Table('user_prefs', engine, 
    Column('pref_id', Integer, primary_key=True),
    Column('user_id', Integer, ForeignKey("users.user_id"), nullable=False),
    Column('pref_name', String(40), nullable=False),
    Column('pref_value', String(100))
)

The specific datatypes, such as Integer, String, etc. are defined in sqlalchemy.types and are automatically pulled in when you import * from sqlalchemy. Note that for Column objects, an altername name can be specified via the "key" parameter; if this parameter is given, then all programmatic references to this Column object will be based on its key, instead of its actual column name.

Once constructed, the Table object provides a clean interface to the table's properties as well as that of its columns:

employees = Table('employees', engine, 
    Column('employee_id', Integer, primary_key=True),
    Column('employee_name', String(60), nullable=False, key='name'),
    Column('employee_dept', Integer, ForeignKey("departments.department_id"))
)

# access the column "EMPLOYEE_ID":
employees.columns.employee_id

# or just
employees.c.employee_id

# via string
employees.c['employee_id']

# iterate through all columns
for c in employees.c:
    # ...
    
# get the table's primary key columns
for primary_key in employees.primary_key:
    # ...

# get the table's foreign key objects:
for fkey in employees.foreign_keys:
    # ...
    
# access the table's SQLEngine object:
employees.engine

# access a column's name, type, nullable, primary key, foreign key
employees.c.employee_id.name
employees.c.employee_id.type
employees.c.employee_id.nullable
employees.c.employee_id.primary_key
employees.c.employee_dept.foreign_key

# get the "key" of a column, which defaults to its name, but can 
# be any user-defined string:
employees.c.name.key

# access a column's table:
employees.c.employee_id.table is employees
>>> True

# get the table related by a foreign key
fcolumn = employees.c.employee_dept.foreign_key.column.table

Metadata objects can also be reflected from tables that already exist in the database. Reflection means based on a table name, the names, datatypes, and attributes of all columns, including foreign keys, will be loaded automatically. This feature is supported by all database engines:

>>> messages = Table('messages', engine, autoload = True)
>>> [c.name for c in messages.columns]
['message_id', 'message_name', 'date']

Note that if a reflected table has a foreign key referencing another table, then the metadata for the related table will be loaded as well, even if it has not been defined by the application:

>>> shopping_cart_items = Table('shopping_cart_items', engine, autoload = True)
>>> print shopping_cart_items.c.cart_id.table.name
shopping_carts

To get direct access to 'shopping_carts', simply instantiate it via the Table constructor. You'll get the same instance of the shopping cart Table as the one that is attached to shopping_cart_items:

>>> shopping_carts = Table('shopping_carts', engine)
>>> shopping_carts is shopping_cart_items.c.cart_id.table.name
True

This works because when the Table constructor is called for a particular name and database engine, if the table has already been created then the instance returned will be the same as the original. This is a singleton constructor:

>>> news_articles = Table('news', engine, 
... Column('article_id', Integer, primary_key = True),
... Column('url', String(250), nullable = False)
... )
>>> othertable = Table('news', engine)
>>> othertable is news_articles
True
back to section top
Creating and Dropping Database Tables

Creating and dropping is easy, just use the create() and drop() methods:

sql
employees = Table('employees', engine, 
    Column('employee_id', Integer, primary_key=True),
    Column('employee_name', String(60), nullable=False, key='name'),
    Column('employee_dept', Integer, ForeignKey("departments.department_id"))
)
employees.create()
 
 sql
 employees.drop()
back to section top
Adapting Tables to Alternate Engines

Occasionally an application will need to reference the same tables within multiple databases simultaneously. Since a Table object is specific to a SQLEngine, an extra method is provided to create copies of the Table object for a different SQLEngine instance, which can represent a different set of connection parameters, or a totally different database driver:

# create two engines
sqlite_engine = create_engine('sqlite', {'filename':'querytest.db'})
postgres_engine = create_engine('postgres', 
                    {'database':'test', 
                    'host':'127.0.0.1', 'user':'scott', 'password':'tiger'})

# load 'users' from the sqlite engine
users = Table('users', sqlite_engine, autoload=True)

# create the same Table object for the other engine
pg_users = users.toengine(postgres_engine)

You can also create tables using a "database neutral" engine, which can serve as a starting point for tables that are then adapted to specific engines:

import sqlalchemy.ansisql as ansisql
generic_engine = ansisql.engine()

users = Table('users', generic_engine, 
    Column('user_id', Integer),
    Column('user_name', String(50))
)

sqlite_engine = create_engine('sqlite', {'filename':'querytest.db'})
sqlite_users = users.toengine(sqlite_engine)
sqlite_users.create()
back to section top
Defining Sequences

A table with a sequence looks like:

table = Table("cartitems", db, 
Column("cart_id", Integer, Sequence('cart_id_seq'), primary_key=True),
Column("description", String(40)),
Column("createdate", DateTime())
    )

Defining a Sequence means that it will be created along with the table.create() call, and more importantly the sequence will be explicitly used when inserting new rows for this table. For databases that dont support sequences, the Sequence object has no effect. A sequence can also be specified with optional=True which indicates the Sequence should only be used on a database that requires an explicit sequence (which currently is just Oracle). A database like Postgres, while it uses sequences to create primary keys, is often used via the SERIAL column option which removes the need for explicit access to the sequence.

back to section top