From: Mike Bayer Date: Tue, 3 Aug 2010 12:51:43 +0000 (-0400) Subject: - dont like that example, its too much for a section that otherwise isn't so X-Git-Tag: rel_0_6_4~60 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5cd1d43bec73db77d26e6f663e2449e911520737;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - dont like that example, its too much for a section that otherwise isn't so in depth --- diff --git a/lib/sqlalchemy/ext/declarative.py b/lib/sqlalchemy/ext/declarative.py index 6922fe08cd..162bc8d6a6 100755 --- a/lib/sqlalchemy/ext/declarative.py +++ b/lib/sqlalchemy/ext/declarative.py @@ -2,13 +2,14 @@ Synopsis ======== -SQLAlchemy object-relational configuration involves the use of -:class:`~sqlalchemy.schema.Table`, :func:`~sqlalchemy.orm.mapper`, and -class objects to define the three areas of configuration. -:mod:`~sqlalchemy.ext.declarative` allows all three types of -configuration to be expressed declaratively on an individual -mapped class. Regular SQLAlchemy schema elements and ORM constructs -are used in most cases. +SQLAlchemy object-relational configuration involves the +combination of :class:`.Table`, :func:`.mapper`, and class +objects to define a mapped class. +:mod:`~sqlalchemy.ext.declarative` allows all three to be +expressed at once within the class declaration. As much as +possible, regular SQLAlchemy schema and ORM constructs are +used directly, so that configuration between "classical" ORM +usage and declarative remain highly similar. As a simple example:: @@ -23,37 +24,43 @@ As a simple example:: Above, the :func:`declarative_base` callable returns a new base class from which all mapped classes should inherit. When the class definition is -completed, a new :class:`~sqlalchemy.schema.Table` and -:class:`~sqlalchemy.orm.mapper` will have been generated, accessible via the -``__table__`` and ``__mapper__`` attributes on the ``SomeClass`` class. +completed, a new :class:`.Table` and +:func:`.mapper` will have been generated. + +The resulting table and mapper are accessible via +``__table__`` and ``__mapper__`` attributes on the +``SomeClass`` class:: + + # access the mapped Table + SomeClass.__table__ + + # access the Mapper + SomeClass.__mapper__ Defining Attributes =================== -In the above example, the :class:`~sqlalchemy.schema.Column` objects are +In the previous example, the :class:`.Column` objects are automatically named with the name of the attribute to which they are assigned. -They can also be explicitly named, and that name does not have to be -the same as name assigned on the class. -The column will be assigned to the :class:`~sqlalchemy.schema.Table` using the -given name, and mapped to the class using the attribute name:: +To name columns explicitly with a name distinct from their mapped attribute, +just give the column a name. Below, column "some_table_id" is mapped to the +"id" attribute of `SomeClass`, but in SQL will be represented as "some_table_id":: class SomeClass(Base): __tablename__ = 'some_table' id = Column("some_table_id", Integer, primary_key=True) - name = Column("name", String(50)) Attributes may be added to the class after its construction, and they will be -added to the underlying :class:`~sqlalchemy.schema.Table` and -:func:`~sqlalchemy.orm.mapper()` definitions as appropriate:: +added to the underlying :class:`.Table` and +:func:`.mapper()` definitions as appropriate:: SomeClass.data = Column('data', Unicode) SomeClass.related = relationship(RelatedInfo) -Classes which are mapped explicitly using -:func:`~sqlalchemy.orm.mapper()` can interact freely with declarative -classes. +Classes which are constructed using declarative can interact freely +with classes that are mapped explicitly with :func:`mapper`. It is recommended, though not required, that all tables share the same underlying :class:`~sqlalchemy.schema.MetaData` object, @@ -349,101 +356,9 @@ and simply pass it to declarative classes:: 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. +Some configuration schemes may find it more appropriate to use ``__table__``, +such as those which already take advantage of the data-driven nature of +:class:`.Table` to customize and/or automate schema definition. Mapper Configuration ====================