Note that the keyword parameters dictionary is required in the tuple
form even if empty.
+Using a Hybrid Approach with __table__
+=======================================
+
As an alternative to ``__tablename__``, a direct
:class:`~sqlalchemy.schema.Table` construct may be used. The
:class:`~sqlalchemy.schema.Column` objects, which in this case require
Column('name', String(50))
)
+``__table__`` provides a more focused point of control for establishing
+table metadata, while still getting most of the benefits of using declarative.
+An application that uses reflection might want to load table metadata elsewhere
+and simply pass it to declarative classes::
+
+ from sqlalchemy.ext.declarative import declarative_base
+
+ Base = declarative_base()
+ Base.metadata.reflect(some_engine)
+
+ class User(Base):
+ __table__ = metadata['user']
+
+ class Address(Base):
+ __table__ = metadata['address']
+
+Example - Formalized Naming Conventions
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The data-driven configuration style of :class:`.Table` makes
+it an easy place to drive application conventions including
+standard columns, constraint names, and column names, for a
+large application that can afford to be more constrained and
+formalized in its configuration. For example, an application
+that provides its own ``make_table()`` function, which
+establishes a table with certain columns, naming conventions
+for constraints, as well as column-creating functions that
+also supply naming conventions, are more easily integrated
+into the ``__table__`` approach than pure declarative::
+
+ '''Illustrate a hybrid declarative/Table approach to schema
+ and model specification.'''
+
+ from sqlalchemy import Table, Column, ForeignKey, \\
+ DateTime, Integer, func, ForeignKeyConstraint
+ from sqlalchemy.ext.declarative import declarative_base
+
+ Base = declarative_base()
+
+ # define some functions for generating
+ # tables and columns with generated naming conventions
+ # for constraints, column names, standard columns
+ def make_table(name, *args, **kw):
+ args += (
+ Column('created', DateTime, default=func.now()),
+ )
+
+ table = Table(name, Base.metadata, *args, **kw)
+ table.primary_key.name = "pk_%s" % name
+ for const in table.constraints:
+ if isinstance(const, ForeignKeyConstraint):
+ fk = list(const.elements)[0]
+ reftable, refcol = fk.target_fullname.split(".")
+ const.name = "fk_%s_%s_%s" % (
+ table.name, fk.parent.name, reftable
+ )
+
+ return table
+
+ def id_column():
+ return Column('id', Integer, primary_key=True)
+
+ def reference_column(tablename):
+ return Column('%s_id' % tablename,
+ Integer,
+ ForeignKey('%s.id' % tablename),
+ nullable=False)
+
+ # elsewhere, in main model code:
+
+ from mymodel import make_table, id_column, ref_column, Base
+ from sqlalchemy import Column, String
+
+ class Order(Base):
+ __table__ = make_table('order',
+ id_column(),
+ reference_column('item'),
+ )
+
+ class Item(Base):
+ __table__ = make_table('item',
+ id_column(),
+ Column("description", String(200))
+ )
+
+Issuing a :meth:`.MetaData.create` for the above applies our naming
+and behavioral conventions::
+
+ CREATE TABLE item (
+ id INTEGER NOT NULL,
+ description VARCHAR(200),
+ created DATETIME DEFAULT CURRENT_TIMESTAMP,
+ CONSTRAINT pk_item PRIMARY KEY (id)
+ )
+
+ CREATE TABLE "order" (
+ id INTEGER NOT NULL,
+ item_id INTEGER NOT NULL,
+ created DATETIME DEFAULT CURRENT_TIMESTAMP,
+ CONSTRAINT pk_order PRIMARY KEY (id),
+ CONSTRAINT fk_order_item_id_item FOREIGN KEY(item_id) REFERENCES item (id)
+ )
+
+The above approach costs more upfront in terms of setting up
+conventions, and is not as "out of the box" as pure
+declarative, not to mention more constrained in its results.
+A similar effect can also be achieved using custom
+metaclasses which subclass :class:`.DeclarativeMeta`, and
+establishing the conventions in the ``__init__`` method of
+the metaclass. The downside there is that metaclasses are
+more tedious to work with.
+
Mapper Configuration
====================
return "ForeignKey(%r)" % self._get_colspec()
def copy(self, schema=None):
- """Produce a copy of this ForeignKey object."""
+ """Produce a copy of this :class:`ForeignKey` object.
+
+ The new :class:`ForeignKey` will not be bound
+ to any :class:`Column`.
+
+ This method is usually used by the internal
+ copy procedures of :class:`Column`, :class:`Table`,
+ and :class:`MetaData`.
+
+ :param schema: The returned :class:`ForeignKey` will
+ reference the original table and column name, qualified
+ by the given string schema name.
+
+ """
return ForeignKey(
self._get_colspec(schema=schema),
)
def _get_colspec(self, schema=None):
+ """Return a string based 'column specification' for this :class:`ForeignKey`.
+
+ This is usually the equivalent of the string-based "tablename.colname"
+ argument first passed to the object's constructor.
+
+ """
if schema:
return schema + "." + self.column.table.name + \
"." + self.column.key
target_fullname = property(_get_colspec)
def references(self, table):
- """Return True if the given table is referenced by this ForeignKey."""
+ """Return True if the given :class:`Table` is referenced by this :class:`ForeignKey`."""
return table.corresponding_column(self.column) is not None
def get_referent(self, table):
- """Return the column in the given table referenced by this ForeignKey.
+ """Return the :class:`.Column` in the given :class:`.Table`
+ referenced by this :class:`ForeignKey`.
- Returns None if this ``ForeignKey`` does not reference the given
- table.
+ Returns None if this :class:`ForeignKey` does not reference the given
+ :class:`Table`.
"""
@util.memoized_property
def column(self):
+ """Return the target :class:`.Column` referenced by this :class:`.ForeignKey`.
+
+ If this :class:`ForeignKey` was created using a
+ string-based target column specification, this
+ attribute will on first access initiate a resolution
+ process to locate the referenced remote
+ :class:`.Column`. The resolution process traverses
+ to the parent :class:`.Column`, :class:`.Table`, and
+ :class:`.MetaData` to proceed - if any of these aren't
+ yet present, an error is raised.
+
+ """
# ForeignKey inits its remote column as late as possible, so tables
# can be defined without dependencies
if isinstance(self._colspec, basestring):