Quick Select: 0.5 | 0.4 | 0.3

SQLAlchemy 0.5 Documentation

Version: 0.5.0beta3 Last Updated: 08/03/08 23:08:40

This section references most major configurational patterns involving the mapper() and relation() functions. It assumes you've worked through the Object Relational Tutorial and know how to construct and use rudimentary mappers and relations.

Mapper Configuration

Full API documentation for the ORM:

module sqlalchemy.orm.

Options for the mapper() function:

mapper().

Customizing Column Properties

The default behavior of a mapper is to assemble all the columns in the mapped Table into mapped object attributes. This behavior can be modified in several ways, as well as enhanced by SQL expressions.

To load only a part of the columns referenced by a table as attributes, use the include_properties and exclude_properties arguments:

mapper(User, users_table, include_properties=['user_id', 'user_name'])

mapper(Address, addresses_table, exclude_properties=['street', 'city', 'state', 'zip'])

To change the name of the attribute mapped to a particular column, place the Column object in the properties dictionary with the desired key:

mapper(User, users_table, properties={ 
   'id' : users_table.c.user_id,
   'name' : users_table.c.user_name,
})

To change the names of all attributes using a prefix, use the column_prefix option. This is useful for classes which wish to add their own property accessors:

mapper(User, users_table, column_prefix='_')

The above will place attribute names such as _user_id, _user_name, _password etc. on the mapped User class.

To place multiple columns which are known to be "synonymous" based on foreign key relationship or join condition into the same mapped attribute, put them together using a list, as below where we map to a Join:

# join users and addresses
usersaddresses = sql.join(users_table, addresses_table, \
    users_table.c.user_id == addresses_table.c.user_id)

mapper(User, usersaddresses, 
   properties = {
           'id':[users_table.c.user_id, addresses_table.c.user_id],
      })
back to section top

Deferred Column Loading

This feature allows particular columns of a table to not be loaded by default, instead being loaded later on when first referenced. It is essentially "column-level lazy loading". This feature is useful when one wants to avoid loading a large text or binary field into memory when it's not needed. Individual columns can be lazy loaded by themselves or placed into groups that lazy-load together.

book_excerpts = Table('books', db, 
    Column('book_id', Integer, primary_key=True),
    Column('title', String(200), nullable=False),
    Column('summary', String(2000)),
    Column('excerpt', String),
    Column('photo', Binary)
  )

class Book(object):
    pass

# define a mapper that will load each of 'excerpt' and 'photo' in 
# separate, individual-row SELECT statements when each attribute
# is first referenced on the individual object instance
mapper(Book, book_excerpts, properties = {
  'excerpt' : deferred(book_excerpts.c.excerpt),
  'photo' : deferred(book_excerpts.c.photo)
})

Deferred columns can be placed into groups so that they load together:

book_excerpts = Table('books', db, 
  Column('book_id', Integer, primary_key=True),
  Column('title', String(200), nullable=False),
  Column('summary', String(2000)),
  Column('excerpt', String),
  Column('photo1', Binary),
  Column('photo2', Binary),
  Column('photo3', Binary)
)

class Book(object):
  pass

# define a mapper with a 'photos' deferred group.  when one photo is referenced,
# all three photos will be loaded in one SELECT statement.  The 'excerpt' will 
# be loaded separately when it is first referenced.
mapper(Book, book_excerpts, properties = {
  'excerpt' : deferred(book_excerpts.c.excerpt),
  'photo1' : deferred(book_excerpts.c.photo1, group='photos'),
  'photo2' : deferred(book_excerpts.c.photo2, group='photos'),
  'photo3' : deferred(book_excerpts.c.photo3, group='photos')
})

You can defer or undefer columns at the Query level using the defer and undefer options:

query = session.query(Book)
query.options(defer('summary')).all()
query.options(undefer('excerpt')).all()

And an entire "deferred group", i.e. which uses the group keyword argument to deferred(), can be undeferred using undefer_group(), sending in the group name:

query = session.query(Book)
query.options(undefer_group('photos')).all()
back to section top

SQL Expressions as Mapped Attributes

To add a SQL clause composed of local or external columns as a read-only, mapped column attribute, use the column_property() function. Any scalar-returning ClauseElement may be used, as long as it has a name attribute; usually, you'll want to call label() to give it a specific name:

mapper(User, users_table, properties={
    'fullname' : column_property(
        (users_table.c.firstname + " " + users_table.c.lastname).label('fullname')
    )
})

Correlated subqueries may be used as well:

mapper(User, users_table, properties={
    'address_count' : column_property(
            select(
                [func.count(addresses_table.c.address_id)], 
                addresses_table.c.user_id==users_table.c.user_id
            ).label('address_count')
        )
})
back to section top

Overriding Attribute Behavior with Synonyms

A common request is the ability to create custom class properties that override the behavior of setting/getting an attribute. As of 0.4.2, the synonym() construct provides an easy way to do this in conjunction with a normal Python property constructs. Below, we re-map the email column of our mapped table to a custom attribute setter/getter, mapping the actual column to the property named _email:

class MyAddress(object):
   def _set_email(self, email):
      self._email = email
   def _get_email(self):
      return self._email
   email = property(_get_email, _set_email)

mapper(MyAddress, addresses_table, properties = {
    'email':synonym('_email', map_column=True)
})

The email attribute is now usable in the same way as any other mapped attribute, including filter expressions, get/set operations, etc.:

address = sess.query(MyAddress).filter(MyAddress.email == 'some address').one()

address.email = 'some other address'
sess.flush()

q = sess.query(MyAddress).filter_by(email='some other address')

If the mapped class does not provide a property, the synonym() construct will create a default getter/setter object automatically.

back to section top

Composite Column Types

Sets of columns can be associated with a single datatype. The ORM treats the group of columns like a single column which accepts and returns objects using the custom datatype you provide. In this example, we'll create a table vertices which stores a pair of x/y coordinates, and a custom datatype Point which is a composite type of an x and y column:

vertices = Table('vertices', metadata, 
    Column('id', Integer, primary_key=True),
    Column('x1', Integer),
    Column('y1', Integer),
    Column('x2', Integer),
    Column('y2', Integer),
    )

The requirements for the custom datatype class are that it have a constructor which accepts positional arguments corresponding to its column format, and also provides a method __composite_values__() which returns the state of the object as a list or tuple, in order of its column-based attributes. It also should supply adequate __eq__() and __ne__() methods which test the equality of two instances:

class Point(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y
    def __composite_values__(self):
        return [self.x, self.y]            
    def __eq__(self, other):
        return other.x == self.x and other.y == self.y
    def __ne__(self, other):
        return not self.__eq__(other)

Setting up the mapping uses the composite() function:

class Vertex(object):
    pass

mapper(Vertex, vertices, properties={
    'start':composite(Point, vertices.c.x1, vertices.c.y1),
    'end':composite(Point, vertices.c.x2, vertices.c.y2)
})

We can now use the Vertex instances as well as querying as though the start and end attributes are regular scalar attributes:

sess = Session()
v = Vertex(Point(3, 4), Point(5, 6))
sess.save(v)

v2 = sess.query(Vertex).filter(Vertex.start == Point(3, 4))

The "equals" comparison operation by default produces an AND of all corresponding columns equated to one another. If you'd like to override this, or define the behavior of other SQL operators for your new type, the composite() function accepts an extension object of type sqlalchemy.orm.PropComparator:

from sqlalchemy.orm import PropComparator
from sqlalchemy import sql

class PointComparator(PropComparator):
    def __gt__(self, other):
        """define the 'greater than' operation"""

        return sql.and_(*[a>b for a, b in
                          zip(self.prop.columns,
                              other.__composite_values__())])

maper(Vertex, vertices, properties={
    'start':composite(Point, vertices.c.x1, vertices.c.y1, comparator=PointComparator),
    'end':composite(Point, vertices.c.x2, vertices.c.y2, comparator=PointComparator)
})
back to section top

Controlling Ordering

As of version 0.5, the ORM does not generate ordering for any query unless explicitly configured.

The "default" ordering for a collection, which applies to list-based collections, can be configured using the order_by keyword argument on relation():

mapper(Address, addresses_table)

# order address objects by address id
mapper(User, users_table, properties = {
    'addresses' : relation(Address, order_by=addresses_table.c.address_id)
})

Note that when using eager loaders with relations, the tables used by the eager load's join are anonymously aliased. You can only order by these columns if you specify it at the relation() level. To control ordering at the query level based on a related table, you join() to that relation, then order by it:

session.query(User).join('addresses').order_by(Address.street)

Ordering for rows loaded through Query is usually specified using the order_by() generative method. There is also an option to set a default ordering for Queries which are against a single mapped entity and where there was no explicit order_by() stated, which is the order_by keyword argument to mapper():

# order by a column
mapper(User, users_table, order_by=users_table.c.user_id)

# order by multiple items
mapper(User, users_table, order_by=[users_table.c.user_id, users_table.c.user_name.desc()])

Above, a Query issued for the User class will use the value of the mapper's order_by setting if the Query itself has no ordering specified.

back to section top

Mapping Class Inheritance Hierarchies

SQLAlchemy supports three forms of inheritance: single table inheritance, where several types of classes are stored in one table, concrete table inheritance, where each type of class is stored in its own table, and joined table inheritance, where the parent/child classes are stored in their own tables that are joined together in a select. Whereas support for single and joined table inheritance is strong, concrete table inheritance is a less common scenario with some particular problems so is not quite as flexible.

When mappers are configured in an inheritance relationship, SQLAlchemy has the ability to load elements "polymorphically", meaning that a single query can return objects of multiple types.

For the following sections, assume this class relationship:

class Employee(object):
    def __init__(self, name):
        self.name = name
    def __repr__(self):
        return self.__class__.__name__ + " " + self.name

class Manager(Employee):
    def __init__(self, name, manager_data):
        self.name = name
        self.manager_data = manager_data
    def __repr__(self):
        return self.__class__.__name__ + " " + self.name + " " +  self.manager_data

class Engineer(Employee):
    def __init__(self, name, engineer_info):
        self.name = name
        self.engineer_info = engineer_info
    def __repr__(self):
        return self.__class__.__name__ + " " + self.name + " " +  self.engineer_info

Joined Table Inheritance

In joined table inheritance, each class along a particular classes' list of parents is represented by a unique table. The total set of attributes for a particular instance is represented as a join along all tables in its inheritance path. Here, we first define a table to represent the Employee class. This table will contain a primary key column (or columns), and a column for each attribute that's represented by Employee. In this case it's just name:

employees = Table('employees', metadata, 
   Column('employee_id', Integer, primary_key=True),
   Column('name', String(50)),
   Column('type', String(30), nullable=False)
)

The table also has a column called type. It is strongly advised in both single- and joined- table inheritance scenarios that the root table contains a column whose sole purpose is that of the discriminator; it stores a value which indicates the type of object represented within the row. The column may be of any desired datatype. While there are some "tricks" to work around the requirement that there be a discriminator column, they are more complicated to configure when one wishes to load polymorphically.

Next we define individual tables for each of Engineer and Manager, which each contain columns that represent the attributes unique to the subclass they represent. Each table also must contain a primary key column (or columns), and in most cases a foreign key reference to the parent table. It is standard practice that the same column is used for both of these roles, and that the column is also named the same as that of the parent table. However this is optional in SQLAlchemy; separate columns may be used for primary key and parent-relation, the column may be named differently than that of the parent, and even a custom join condition can be specified between parent and child tables instead of using a foreign key. In joined table inheritance, the primary key of an instance is always represented by the primary key of the base table only (new in SQLAlchemy 0.4).

engineers = Table('engineers', metadata, 
   Column('employee_id', Integer, ForeignKey('employees.employee_id'), primary_key=True),
   Column('engineer_info', String(50)),
)

managers = Table('managers', metadata, 
   Column('employee_id', Integer, ForeignKey('employees.employee_id'), primary_key=True),
   Column('manager_data', String(50)),
)

We then configure mappers as usual, except we use some additional arguments to indicate the inheritance relationship, the polymorphic discriminator column, and the polymorphic identity of each class; this is the value that will be stored in the polymorphic discriminator column.

mapper(Employee, employees, polymorphic_on=employees.c.type, polymorphic_identity='employee')
mapper(Engineer, engineers, inherits=Employee, polymorphic_identity='engineer')
mapper(Manager, managers, inherits=Employee, polymorphic_identity='manager')

And that's it. Querying against Employee will return a combination of Employee, Engineer and Manager objects.

Polymorphic Querying Strategies

The Query object includes some helper functionality when dealing with joined-table inheritance mappings. These are the with_polymorphic() and of_type() methods, both of which are introduced in version 0.4.4.

The with_polymorphic() method affects the specific subclass tables which the Query selects from. Normally, a query such as this:

session.query(Employee).filter(Employee.name=='ed')

Selects only from the employees table. The criterion we use in filter() and other methods will generate WHERE criterion against this table. What if we wanted to load Employee objects but also wanted to use criterion against Engineer ? We could just query against the Engineer class instead. But, if we were using criterion which filters among more than one subclass (subclasses which do not inherit directly from one to the other), we'd like to select from an outer join of all those tables. The with_polymorphic() method can tell Query which joined-table subclasses we want to select for:

session.query(Employee).with_polymorphic(Engineer).filter(Engineer.engineer_info=='some info')

Even without criterion, the with_polymorphic() method has the added advantage that instances are loaded from all of their tables in one result set. Such as, to optimize the loading of all Employee objects, with_polymorphic() accepts '*' as a wildcard indicating that all subclass tables should be joined:

session.query(Employee).with_polymorphic('*').all()

with_polymorphic() is an effective query-level alternative to the existing select_table option available on mapper().

Next is a way to join along relation paths while narrowing the criterion to specific subclasses. Suppose the employees table represents a collection of employees which are associated with a Company object. We'll add a company_id column to the employees table and a new table companies:

companies = Table('companies', metadata,
   Column('company_id', Integer, primary_key=True),
   Column('name', String(50))
   )

employees = Table('employees', metadata, 
  Column('employee_id', Integer, primary_key=True),
  Column('name', String(50)),
  Column('type', String(30), nullable=False),
  Column('company_id', Integer, ForeignKey('companies.company_id'))
)

class Company(object):
    pass

mapper(Company, companies, properties={
    'employees':relation(Employee)
})

If we wanted to join from Company to not just Employee but specifically Engineers, using the join() method or any() or has() operators will by default create a join from companies to employees, without including engineers or managers in the mix. If we wish to have criterion which is specifically against the Engineer class, we can tell those methods to join or subquery against the full set of tables representing the subclass using the of_type() opertator:

session.query(Company).join(Company.employees.of_type(Engineer)).filter(Engineer.engineer_info=='someinfo')

A longhand notation, introduced in 0.4.3, is also available, which involves spelling out the full target selectable within a 2-tuple:

session.query(Company).join(('employees', employees.join(engineers))).filter(Engineer.engineer_info=='someinfo')

The second notation allows more flexibility, such as joining to any group of subclass tables:

session.query(Company).join(('employees', employees.outerjoin(engineers).outerjoin(managers))).\
    filter(or_(Engineer.engineer_info=='someinfo', Manager.manager_data=='somedata'))

The any() and has() operators also can be used with of_type() when the embedded criterion is in terms of a subclass:

session.query(Company).filter(Company.employees.of_type(Engineer).any(Engineer.engineer_info=='someinfo')).all()

Note that the any() and has() are both shorthand for a correlated EXISTS query. To build one by hand looks like:

session.query(Company).filter(
    exists([1], 
        and_(Engineer.engineer_info=='someinfo', employees.c.company_id==companies.c.company_id), 
        from_obj=employees.join(engineers)
    )
).all()

The EXISTS subquery above selects from the join of employees to engineers, and also specifies criterion which correlates the EXISTS subselect back to the parent companies table.

back to section top

Optimizing Joined Table Loads

When loading fresh from the database, the joined-table setup above will query from the parent table first, then for each row will issue a second query to the child table. For example, for a load of five rows with Employee id 3, Manager ids 1 and 5 and Engineer ids 2 and 4, will produce queries along the lines of this example:

session.query(Employee).all()
SELECT employees.employee_id AS employees_employee_id, employees.name AS employees_name, employees.type AS employees_type
FROM employees ORDER BY employees.oid
[]
SELECT managers.employee_id AS managers_employee_id, managers.manager_data AS managers_manager_data
FROM managers
WHERE ? = managers.employee_id
[5]
SELECT engineers.employee_id AS engineers_employee_id, engineers.engineer_info AS engineers_engineer_info
FROM engineers
WHERE ? = engineers.employee_id
[2]
SELECT engineers.employee_id AS engineers_employee_id, engineers.engineer_info AS engineers_engineer_info
FROM engineers
WHERE ? = engineers.employee_id
[4]
SELECT managers.employee_id AS managers_employee_id, managers.manager_data AS managers_manager_data
FROM managers
WHERE ? = managers.employee_id
[1]

The above query works well for a get() operation, since it limits the queries to only the tables directly involved in fetching a single instance. For instances which are already present in the session, the secondary table load is not needed. However, the above loading style is not efficient for loading large groups of objects, as it incurs separate queries for each parent row.

One way to reduce the number of "secondary" loads of child rows is to "defer" them, using polymorphic_fetch='deferred':

mapper(Employee, employees, polymorphic_on=employees.c.type, \
    polymorphic_identity='employee', polymorphic_fetch='deferred')
mapper(Engineer, engineers, inherits=Employee, polymorphic_identity='engineer')
mapper(Manager, managers, inherits=Employee, polymorphic_identity='manager')

The above configuration queries in the same manner as earlier, except the load of each "secondary" table occurs only when attributes referencing those columns are first referenced on the loaded instance. This style of loading is very efficient for cases where large selects of items occur, but a detailed "drill down" of extra inherited properties is less common.

More commonly, an all-at-once load may be achieved by constructing a query which combines all three tables together. The easiest way to do this as of version 0.4.4 is to use the with_polymorphic() query method which will automatically join in the classes desired:

query = session.query(Employee).with_polymorphic([Engineer, Manager])

Which produces a query like the following:

query.all()
SELECT employees.employee_id AS employees_employee_id, engineers.employee_id AS engineers_employee_id, managers.employee_id AS managers_employee_id, employees.name AS employees_name, employees.type AS employees_type, engineers.engineer_info AS engineers_engineer_info, managers.manager_data AS managers_manager_data
FROM employees LEFT OUTER JOIN engineers ON employees.employee_id = engineers.employee_id LEFT OUTER JOIN managers ON employees.employee_id = managers.employee_id ORDER BY employees.oid
[]

with_polymorphic() accepts a single class or mapper, a list of classes/mappers, or the string '*' to indicate all subclasses. It also accepts a second argument selectable which replaces the automatic join creation and instead selects directly from the selectable given. This can allow polymorphic loads from a variety of inheritance schemes including concrete tables, if the appropriate unions are constructed.

Similar behavior as provided by with_polymorphic() can be configured at the mapper level so that any user-defined query is used by default in order to load instances. The select_table argument references an arbitrary selectable which the mapper will use for load operations (it has no impact on save operations). Any selectable can be used for this, such as a UNION of tables. For joined table inheritance, the easiest method is to use OUTER JOIN:

join = employees.outerjoin(engineers).outerjoin(managers)

mapper(Employee, employees, polymorphic_on=employees.c.type, \
    polymorphic_identity='employee', select_table=join)
mapper(Engineer, engineers, inherits=Employee, polymorphic_identity='engineer')
mapper(Manager, managers, inherits=Employee, polymorphic_identity='manager')

The above mapping will produce a query similar to that of with_polymorphic('*') for every query of Employee objects.

When select_table is used, with_polymorphic() still overrides its usage at the query level. For example, if select_table were configured to load from a join of multiple tables, using with_polymorphic(Employee) will limit the list of tables selected from to just the base table (as always, tables which don't get loaded in the first pass will be loaded on an as-needed basis).

back to section top

Single Table Inheritance

Single table inheritance is where the attributes of the base class as well as all subclasses are represented within a single table. A column is present in the table for every attribute mapped to the base class and all subclasses; the columns which correspond to a single subclass are nullable. This configuration looks much like joined-table inheritance except there's only one table. In this case, a type column is required, as there would be no other way to discriminate between classes. The table is specified in the base mapper only; for the inheriting classes, leave their table parameter blank:

employees_table = Table('employees', metadata, 
    Column('employee_id', Integer, primary_key=True),
    Column('name', String(50)),
    Column('manager_data', String(50)),
    Column('engineer_info', String(50)),
    Column('type', String(20), nullable=False)
)

employee_mapper = mapper(Employee, employees_table, \
    polymorphic_on=employees_table.c.type, polymorphic_identity='employee')
manager_mapper = mapper(Manager, inherits=employee_mapper, polymorphic_identity='manager')
engineer_mapper = mapper(Engineer, inherits=employee_mapper, polymorphic_identity='engineer')

Note that the mappers for the derived classes Manager and Engineer omit the specification of their associated table, as it is inherited from the employee_mapper. Omitting the table specification for derived mappers in single-table inheritance is required.

back to section top

Concrete Table Inheritance

This form of inheritance maps each class to a distinct table, as below:

employees_table = Table('employees', metadata, 
    Column('employee_id', Integer, primary_key=True),
    Column('name', String(50)),
)

managers_table = Table('managers', metadata, 
    Column('employee_id', Integer, primary_key=True),
    Column('name', String(50)),
    Column('manager_data', String(50)),
)

engineers_table = Table('engineers', metadata, 
    Column('employee_id', Integer, primary_key=True),
    Column('name', String(50)),
    Column('engineer_info', String(50)),
)

Notice in this case there is no type column. If polymorphic loading is not required, there's no advantage to using inherits here; you just define a separate mapper for each class.

mapper(Employee, employees_table)
mapper(Manager, managers_table)
mapper(Engineer, engineers_table)

To load polymorphically, the select_table argument is currently required. In this case we must construct a UNION of all three tables. SQLAlchemy includes a helper function to create these called polymorphic_union, which will map all the different columns into a structure of selects with the same numbers and names of columns, and also generate a virtual type column for each subselect:

pjoin = polymorphic_union({
    'employee':employees_table,
    'manager':managers_table,
    'engineer':engineers_table
}, 'type', 'pjoin')

employee_mapper = mapper(Employee, employees_table, select_table=pjoin, \
    polymorphic_on=pjoin.c.type, polymorphic_identity='employee')
manager_mapper = mapper(Manager, managers_table, inherits=employee_mapper, \
    concrete=True, polymorphic_identity='manager')
engineer_mapper = mapper(Engineer, engineers_table, inherits=employee_mapper, \
    concrete=True, polymorphic_identity='engineer')

Upon select, the polymorphic union produces a query like this:

session.query(Employee).all()
SELECT pjoin.type AS pjoin_type, pjoin.manager_data AS pjoin_manager_data, pjoin.employee_id AS pjoin_employee_id,
pjoin.name AS pjoin_name, pjoin.engineer_info AS pjoin_engineer_info
FROM (
SELECT employees.employee_id AS employee_id, CAST(NULL AS VARCHAR(50)) AS manager_data, employees.name AS name,
CAST(NULL AS VARCHAR(50)) AS engineer_info, 'employee' AS type
FROM employees
UNION ALL
SELECT managers.employee_id AS employee_id, managers.manager_data AS manager_data, managers.name AS name,
CAST(NULL AS VARCHAR(50)) AS engineer_info, 'manager' AS type
FROM managers
UNION ALL
SELECT engineers.employee_id AS employee_id, CAST(NULL AS VARCHAR(50)) AS manager_data, engineers.name AS name,
engineers.engineer_info AS engineer_info, 'engineer' AS type
FROM engineers
) AS pjoin ORDER BY pjoin.oid
[]

back to section top

Using Relations with Inheritance

Both joined-table and single table inheritance scenarios produce mappings which are usable in relation() functions; that is, it's possible to map a parent object to a child object which is polymorphic. Similarly, inheriting mappers can have relation()s of their own at any level, which are inherited to each child class. The only requirement for relations is that there is a table relationship between parent and child. An example is the following modification to the joined table inheritance example, which sets a bi-directional relationship between Employee and Company:

employees_table = Table('employees', metadata, 
    Column('employee_id', Integer, primary_key=True),
    Column('name', String(50)),
    Column('company_id', Integer, ForeignKey('companies.company_id'))
)

companies = Table('companies', metadata, 
   Column('company_id', Integer, primary_key=True),
   Column('name', String(50)))

class Company(object):
    pass

mapper(Company, companies, properties={
   'employees': relation(Employee, backref='company')
})

SQLAlchemy has a lot of experience in this area; the optimized "outer join" approach can be used freely for parent and child relationships, eager loads are fully useable, query aliasing and other tricks are fully supported as well.

In a concrete inheritance scenario, mapping relation()s is more difficult since the distinct classes do not share a table. In this case, you can establish a relationship from parent to child if a join condition can be constructed from parent to child, if each child table contains a foreign key to the parent:

companies = Table('companies', metadata, 
   Column('id', Integer, primary_key=True),
   Column('name', String(50