summaryrefslogtreecommitdiff
path: root/doc/build/core/metadata.rst
diff options
context:
space:
mode:
Diffstat (limited to 'doc/build/core/metadata.rst')
-rw-r--r--doc/build/core/metadata.rst330
1 files changed, 330 insertions, 0 deletions
diff --git a/doc/build/core/metadata.rst b/doc/build/core/metadata.rst
new file mode 100644
index 000000000..d6fc8c6af
--- /dev/null
+++ b/doc/build/core/metadata.rst
@@ -0,0 +1,330 @@
+.. _metadata_toplevel:
+
+.. _metadata_describing_toplevel:
+
+.. _metadata_describing:
+
+==================================
+Describing Databases with MetaData
+==================================
+
+.. module:: sqlalchemy.schema
+
+This section discusses the fundamental :class:`.Table`, :class:`.Column`
+and :class:`.MetaData` objects.
+
+A collection of metadata entities is stored in an object aptly named
+:class:`~sqlalchemy.schema.MetaData`::
+
+ from sqlalchemy import *
+
+ metadata = MetaData()
+
+:class:`~sqlalchemy.schema.MetaData` is a container object that keeps together
+many different features of a database (or multiple databases) being described.
+
+To represent a table, use the :class:`~sqlalchemy.schema.Table` class. Its two
+primary arguments are the table name, then the
+:class:`~sqlalchemy.schema.MetaData` object which it will be associated with.
+The remaining positional arguments are mostly
+:class:`~sqlalchemy.schema.Column` objects describing each column::
+
+ user = Table('user', metadata,
+ Column('user_id', Integer, primary_key = True),
+ Column('user_name', String(16), nullable = False),
+ Column('email_address', String(60)),
+ Column('password', String(20), nullable = False)
+ )
+
+Above, a table called ``user`` is described, which contains four columns. The
+primary key of the table consists of the ``user_id`` column. Multiple columns
+may be assigned the ``primary_key=True`` flag which denotes a multi-column
+primary key, known as a *composite* primary key.
+
+Note also that each column describes its datatype using objects corresponding
+to genericized types, such as :class:`~sqlalchemy.types.Integer` and
+:class:`~sqlalchemy.types.String`. SQLAlchemy features dozens of types of
+varying levels of specificity as well as the ability to create custom types.
+Documentation on the type system can be found at :ref:`types`.
+
+Accessing Tables and Columns
+----------------------------
+
+The :class:`~sqlalchemy.schema.MetaData` object contains all of the schema
+constructs we've associated with it. It supports a few methods of accessing
+these table objects, such as the ``sorted_tables`` accessor which returns a
+list of each :class:`~sqlalchemy.schema.Table` object in order of foreign key
+dependency (that is, each table is preceded by all tables which it
+references)::
+
+ >>> for t in metadata.sorted_tables:
+ ... print t.name
+ user
+ user_preference
+ invoice
+ invoice_item
+
+In most cases, individual :class:`~sqlalchemy.schema.Table` objects have been
+explicitly declared, and these objects are typically accessed directly as
+module-level variables in an application. Once a
+:class:`~sqlalchemy.schema.Table` has been defined, it has a full set of
+accessors which allow inspection of its properties. Given the following
+:class:`~sqlalchemy.schema.Table` definition::
+
+ employees = Table('employees', metadata,
+ Column('employee_id', Integer, primary_key=True),
+ Column('employee_name', String(60), nullable=False),
+ Column('employee_dept', Integer, ForeignKey("departments.department_id"))
+ )
+
+Note the :class:`~sqlalchemy.schema.ForeignKey` object used in this table -
+this construct defines a reference to a remote table, and is fully described
+in :ref:`metadata_foreignkeys`. Methods of accessing information about this
+table include::
+
+ # 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:
+ print c
+
+ # get the table's primary key columns
+ for primary_key in employees.primary_key:
+ print primary_key
+
+ # get the table's foreign key objects:
+ for fkey in employees.foreign_keys:
+ print fkey
+
+ # access the table's MetaData:
+ employees.metadata
+
+ # access the table's bound Engine or Connection, if its MetaData is bound:
+ employees.bind
+
+ # 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_keys
+
+ # get the "key" of a column, which defaults to its name, but can
+ # be any user-defined string:
+ employees.c.employee_name.key
+
+ # access a column's table:
+ employees.c.employee_id.table is employees
+
+ # get the table related by a foreign key
+ list(employees.c.employee_dept.foreign_keys)[0].column.table
+
+Creating and Dropping Database Tables
+-------------------------------------
+
+Once you've defined some :class:`~sqlalchemy.schema.Table` objects, assuming
+you're working with a brand new database one thing you might want to do is
+issue CREATE statements for those tables and their related constructs (as an
+aside, it's also quite possible that you *don't* want to do this, if you
+already have some preferred methodology such as tools included with your
+database or an existing scripting system - if that's the case, feel free to
+skip this section - SQLAlchemy has no requirement that it be used to create
+your tables).
+
+The usual way to issue CREATE is to use
+:func:`~sqlalchemy.schema.MetaData.create_all` on the
+:class:`~sqlalchemy.schema.MetaData` object. This method will issue queries
+that first check for the existence of each individual table, and if not found
+will issue the CREATE statements:
+
+ .. sourcecode:: python+sql
+
+ engine = create_engine('sqlite:///:memory:')
+
+ metadata = MetaData()
+
+ user = Table('user', metadata,
+ 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', metadata,
+ Column('pref_id', Integer, primary_key=True),
+ Column('user_id', Integer, ForeignKey("user.user_id"), nullable=False),
+ Column('pref_name', String(40), nullable=False),
+ Column('pref_value', String(100))
+ )
+
+ {sql}metadata.create_all(engine)
+ PRAGMA table_info(user){}
+ CREATE TABLE user(
+ user_id INTEGER NOT NULL PRIMARY KEY,
+ user_name VARCHAR(16) NOT NULL,
+ email_address VARCHAR(60),
+ password VARCHAR(20) NOT NULL
+ )
+ PRAGMA table_info(user_prefs){}
+ CREATE TABLE user_prefs(
+ pref_id INTEGER NOT NULL PRIMARY KEY,
+ user_id INTEGER NOT NULL REFERENCES user(user_id),
+ pref_name VARCHAR(40) NOT NULL,
+ pref_value VARCHAR(100)
+ )
+
+:func:`~sqlalchemy.schema.MetaData.create_all` creates foreign key constraints
+between tables usually inline with the table definition itself, and for this
+reason it also generates the tables in order of their dependency. There are
+options to change this behavior such that ``ALTER TABLE`` is used instead.
+
+Dropping all tables is similarly achieved using the
+:func:`~sqlalchemy.schema.MetaData.drop_all` method. This method does the
+exact opposite of :func:`~sqlalchemy.schema.MetaData.create_all` - the
+presence of each table is checked first, and tables are dropped in reverse
+order of dependency.
+
+Creating and dropping individual tables can be done via the ``create()`` and
+``drop()`` methods of :class:`~sqlalchemy.schema.Table`. These methods by
+default issue the CREATE or DROP regardless of the table being present:
+
+.. sourcecode:: python+sql
+
+ engine = create_engine('sqlite:///:memory:')
+
+ meta = MetaData()
+
+ employees = Table('employees', meta,
+ Column('employee_id', Integer, primary_key=True),
+ Column('employee_name', String(60), nullable=False, key='name'),
+ Column('employee_dept', Integer, ForeignKey("departments.department_id"))
+ )
+ {sql}employees.create(engine)
+ CREATE TABLE employees(
+ employee_id SERIAL NOT NULL PRIMARY KEY,
+ employee_name VARCHAR(60) NOT NULL,
+ employee_dept INTEGER REFERENCES departments(department_id)
+ )
+ {}
+
+``drop()`` method:
+
+.. sourcecode:: python+sql
+
+ {sql}employees.drop(engine)
+ DROP TABLE employees
+ {}
+
+To enable the "check first for the table existing" logic, add the
+``checkfirst=True`` argument to ``create()`` or ``drop()``::
+
+ employees.create(engine, checkfirst=True)
+ employees.drop(engine, checkfirst=False)
+
+.. _schema_migrations:
+
+Altering Schemas through Migrations
+-----------------------------------
+
+While SQLAlchemy directly supports emitting CREATE and DROP statements for schema
+constructs, the ability to alter those constructs, usually via the ALTER statement
+as well as other database-specific constructs, is outside of the scope of SQLAlchemy
+itself. While it's easy enough to emit ALTER statements and similar by hand,
+such as by passing a string to :meth:`.Connection.execute` or by using the
+:class:`.DDL` construct, it's a common practice to automate the maintenance of
+database schemas in relation to application code using schema migration tools.
+
+There are two major migration tools available for SQLAlchemy:
+
+* `Alembic <http://alembic.readthedocs.org>`_ - Written by the author of SQLAlchemy,
+ Alembic features a highly customizable environment and a minimalistic usage pattern,
+ supporting such features as transactional DDL, automatic generation of "candidate"
+ migrations, an "offline" mode which generates SQL scripts, and support for branch
+ resolution.
+* `SQLAlchemy-Migrate <http://code.google.com/p/sqlalchemy-migrate/>`_ - The original
+ migration tool for SQLAlchemy, SQLAlchemy-Migrate is widely used and continues
+ under active development. SQLAlchemy-Migrate includes features such as
+ SQL script generation, ORM class generation, ORM model comparison, and extensive
+ support for SQLite migrations.
+
+
+Specifying the Schema Name
+---------------------------
+
+Some databases support the concept of multiple schemas. A
+:class:`~sqlalchemy.schema.Table` can reference this by specifying the
+``schema`` keyword argument::
+
+ financial_info = Table('financial_info', meta,
+ Column('id', Integer, primary_key=True),
+ Column('value', String(100), nullable=False),
+ schema='remote_banks'
+ )
+
+Within the :class:`~sqlalchemy.schema.MetaData` collection, this table will be
+identified by the combination of ``financial_info`` and ``remote_banks``. If
+another table called ``financial_info`` is referenced without the
+``remote_banks`` schema, it will refer to a different
+:class:`~sqlalchemy.schema.Table`. :class:`~sqlalchemy.schema.ForeignKey`
+objects can specify references to columns in this table using the form
+``remote_banks.financial_info.id``.
+
+The ``schema`` argument should be used for any name qualifiers required,
+including Oracle's "owner" attribute and similar. It also can accommodate a
+dotted name for longer schemes::
+
+ schema="dbo.scott"
+
+Backend-Specific Options
+------------------------
+
+:class:`~sqlalchemy.schema.Table` supports database-specific options. For
+example, MySQL has different table backend types, including "MyISAM" and
+"InnoDB". This can be expressed with :class:`~sqlalchemy.schema.Table` using
+``mysql_engine``::
+
+ addresses = Table('engine_email_addresses', meta,
+ Column('address_id', Integer, primary_key = True),
+ Column('remote_user_id', Integer, ForeignKey(users.c.user_id)),
+ Column('email_address', String(20)),
+ mysql_engine='InnoDB'
+ )
+
+Other backends may support table-level options as well - these would be
+described in the individual documentation sections for each dialect.
+
+Column, Table, MetaData API
+---------------------------
+
+.. autoclass:: Column
+ :members:
+ :inherited-members:
+ :undoc-members:
+
+
+.. autoclass:: MetaData
+ :members:
+ :undoc-members:
+
+
+.. autoclass:: SchemaItem
+ :members:
+
+.. autoclass:: Table
+ :members:
+ :inherited-members:
+ :undoc-members:
+
+
+.. autoclass:: ThreadLocalMetaData
+ :members:
+ :undoc-members:
+
+