PageRenderTime 720ms CodeModel.GetById 24ms app.highlight 318ms RepoModel.GetById 1ms app.codeStats 0ms

/SQLAlchemy-0.7.8/lib/sqlalchemy/ext/declarative.py

#
Python | 1768 lines | 1720 code | 20 blank | 28 comment | 40 complexity | 1c4bc993b19160cf31f0442b2de59253 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1# ext/declarative.py
   2# Copyright (C) 2005-2012 the SQLAlchemy authors and contributors <see AUTHORS file>
   3#
   4# This module is part of SQLAlchemy and is released under
   5# the MIT License: http://www.opensource.org/licenses/mit-license.php
   6
   7"""
   8Synopsis
   9========
  10
  11SQLAlchemy object-relational configuration involves the
  12combination of :class:`.Table`, :func:`.mapper`, and class
  13objects to define a mapped class.
  14:mod:`~sqlalchemy.ext.declarative` allows all three to be
  15expressed at once within the class declaration. As much as
  16possible, regular SQLAlchemy schema and ORM constructs are
  17used directly, so that configuration between "classical" ORM
  18usage and declarative remain highly similar.
  19
  20As a simple example::
  21
  22    from sqlalchemy.ext.declarative import declarative_base
  23
  24    Base = declarative_base()
  25
  26    class SomeClass(Base):
  27        __tablename__ = 'some_table'
  28        id = Column(Integer, primary_key=True)
  29        name =  Column(String(50))
  30
  31Above, the :func:`declarative_base` callable returns a new base class from
  32which all mapped classes should inherit. When the class definition is
  33completed, a new :class:`.Table` and
  34:func:`.mapper` will have been generated.
  35
  36The resulting table and mapper are accessible via
  37``__table__`` and ``__mapper__`` attributes on the
  38``SomeClass`` class::
  39
  40    # access the mapped Table
  41    SomeClass.__table__
  42
  43    # access the Mapper
  44    SomeClass.__mapper__
  45
  46Defining Attributes
  47===================
  48
  49In the previous example, the :class:`.Column` objects are
  50automatically named with the name of the attribute to which they are
  51assigned.
  52
  53To name columns explicitly with a name distinct from their mapped attribute,
  54just give the column a name.  Below, column "some_table_id" is mapped to the 
  55"id" attribute of `SomeClass`, but in SQL will be represented as "some_table_id"::
  56
  57    class SomeClass(Base):
  58        __tablename__ = 'some_table'
  59        id = Column("some_table_id", Integer, primary_key=True)
  60
  61Attributes may be added to the class after its construction, and they will be
  62added to the underlying :class:`.Table` and
  63:func:`.mapper()` definitions as appropriate::
  64
  65    SomeClass.data = Column('data', Unicode)
  66    SomeClass.related = relationship(RelatedInfo)
  67
  68Classes which are constructed using declarative can interact freely
  69with classes that are mapped explicitly with :func:`mapper`.
  70
  71It is recommended, though not required, that all tables 
  72share the same underlying :class:`~sqlalchemy.schema.MetaData` object,
  73so that string-configured :class:`~sqlalchemy.schema.ForeignKey`
  74references can be resolved without issue.
  75
  76Accessing the MetaData
  77=======================
  78
  79The :func:`declarative_base` base class contains a
  80:class:`.MetaData` object where newly defined
  81:class:`.Table` objects are collected. This object is
  82intended to be accessed directly for
  83:class:`.MetaData`-specific operations. Such as, to issue
  84CREATE statements for all tables::
  85
  86    engine = create_engine('sqlite://')
  87    Base.metadata.create_all(engine)
  88
  89The usual techniques of associating :class:`.MetaData:` with :class:`.Engine`
  90apply, such as assigning to the ``bind`` attribute::
  91
  92    Base.metadata.bind = create_engine('sqlite://')
  93
  94To associate the engine with the :func:`declarative_base` at time
  95of construction, the ``bind`` argument is accepted::
  96
  97    Base = declarative_base(bind=create_engine('sqlite://'))
  98
  99:func:`declarative_base` can also receive a pre-existing
 100:class:`.MetaData` object, which allows a
 101declarative setup to be associated with an already 
 102existing traditional collection of :class:`~sqlalchemy.schema.Table`
 103objects:: 
 104
 105    mymetadata = MetaData()
 106    Base = declarative_base(metadata=mymetadata)
 107
 108Configuring Relationships
 109=========================
 110
 111Relationships to other classes are done in the usual way, with the added
 112feature that the class specified to :func:`~sqlalchemy.orm.relationship`
 113may be a string name.  The "class registry" associated with ``Base``
 114is used at mapper compilation time to resolve the name into the actual
 115class object, which is expected to have been defined once the mapper
 116configuration is used:: 
 117
 118    class User(Base):
 119        __tablename__ = 'users'
 120
 121        id = Column(Integer, primary_key=True)
 122        name = Column(String(50))
 123        addresses = relationship("Address", backref="user")
 124
 125    class Address(Base):
 126        __tablename__ = 'addresses'
 127
 128        id = Column(Integer, primary_key=True)
 129        email = Column(String(50))
 130        user_id = Column(Integer, ForeignKey('users.id'))
 131
 132Column constructs, since they are just that, are immediately usable,
 133as below where we define a primary join condition on the ``Address``
 134class using them:: 
 135
 136    class Address(Base):
 137        __tablename__ = 'addresses'
 138
 139        id = Column(Integer, primary_key=True)
 140        email = Column(String(50))
 141        user_id = Column(Integer, ForeignKey('users.id'))
 142        user = relationship(User, primaryjoin=user_id == User.id)
 143
 144In addition to the main argument for :func:`~sqlalchemy.orm.relationship`,
 145other arguments which depend upon the columns present on an as-yet
 146undefined class may also be specified as strings.  These strings are
 147evaluated as Python expressions.  The full namespace available within
 148this evaluation includes all classes mapped for this declarative base,
 149as well as the contents of the ``sqlalchemy`` package, including
 150expression functions like :func:`~sqlalchemy.sql.expression.desc` and
 151:attr:`~sqlalchemy.sql.expression.func`:: 
 152
 153    class User(Base):
 154        # ....
 155        addresses = relationship("Address",
 156                             order_by="desc(Address.email)", 
 157                             primaryjoin="Address.user_id==User.id")
 158
 159As an alternative to string-based attributes, attributes may also be 
 160defined after all classes have been created.  Just add them to the target
 161class after the fact::
 162
 163    User.addresses = relationship(Address,
 164                              primaryjoin=Address.user_id==User.id)
 165
 166Configuring Many-to-Many Relationships
 167======================================
 168
 169Many-to-many relationships are also declared in the same way
 170with declarative as with traditional mappings. The
 171``secondary`` argument to
 172:func:`.relationship` is as usual passed a 
 173:class:`.Table` object, which is typically declared in the 
 174traditional way.  The :class:`.Table` usually shares
 175the :class:`.MetaData` object used by the declarative base::
 176
 177    keywords = Table(
 178        'keywords', Base.metadata,
 179        Column('author_id', Integer, ForeignKey('authors.id')),
 180        Column('keyword_id', Integer, ForeignKey('keywords.id'))
 181        )
 182
 183    class Author(Base):
 184        __tablename__ = 'authors'
 185        id = Column(Integer, primary_key=True)
 186        keywords = relationship("Keyword", secondary=keywords)
 187
 188Like other :func:`.relationship` arguments, a string is accepted as well, 
 189passing the string name of the table as defined in the ``Base.metadata.tables``
 190collection::
 191
 192    class Author(Base):
 193        __tablename__ = 'authors'
 194        id = Column(Integer, primary_key=True)
 195        keywords = relationship("Keyword", secondary="keywords")
 196
 197As with traditional mapping, its generally not a good idea to use 
 198a :class:`.Table` as the "secondary" argument which is also mapped to
 199a class, unless the :class:`.relationship` is declared with ``viewonly=True``.
 200Otherwise, the unit-of-work system may attempt duplicate INSERT and
 201DELETE statements against the underlying table.
 202
 203.. _declarative_sql_expressions:
 204
 205Defining SQL Expressions
 206========================
 207
 208See :ref:`mapper_sql_expressions` for examples on declaratively
 209mapping attributes to SQL expressions.
 210
 211.. _declarative_table_args:
 212
 213Table Configuration
 214===================
 215
 216Table arguments other than the name, metadata, and mapped Column
 217arguments are specified using the ``__table_args__`` class attribute.
 218This attribute accommodates both positional as well as keyword
 219arguments that are normally sent to the
 220:class:`~sqlalchemy.schema.Table` constructor.
 221The attribute can be specified in one of two forms. One is as a
 222dictionary:: 
 223
 224    class MyClass(Base):
 225        __tablename__ = 'sometable'
 226        __table_args__ = {'mysql_engine':'InnoDB'}
 227
 228The other, a tuple, where each argument is positional
 229(usually constraints)::
 230
 231    class MyClass(Base):
 232        __tablename__ = 'sometable'
 233        __table_args__ = (
 234                ForeignKeyConstraint(['id'], ['remote_table.id']),
 235                UniqueConstraint('foo'),
 236                )
 237
 238Keyword arguments can be specified with the above form by 
 239specifying the last argument as a dictionary::
 240
 241    class MyClass(Base):
 242        __tablename__ = 'sometable'
 243        __table_args__ = (
 244                ForeignKeyConstraint(['id'], ['remote_table.id']),
 245                UniqueConstraint('foo'),
 246                {'autoload':True}
 247                )
 248
 249Using a Hybrid Approach with __table__
 250=======================================
 251
 252As an alternative to ``__tablename__``, a direct
 253:class:`~sqlalchemy.schema.Table` construct may be used.  The
 254:class:`~sqlalchemy.schema.Column` objects, which in this case require
 255their names, will be added to the mapping just like a regular mapping
 256to a table:: 
 257
 258    class MyClass(Base):
 259        __table__ = Table('my_table', Base.metadata,
 260            Column('id', Integer, primary_key=True),
 261            Column('name', String(50))
 262        )
 263
 264``__table__`` provides a more focused point of control for establishing
 265table metadata, while still getting most of the benefits of using declarative.
 266An application that uses reflection might want to load table metadata elsewhere
 267and pass it to declarative classes::
 268
 269    from sqlalchemy.ext.declarative import declarative_base
 270
 271    Base = declarative_base()
 272    Base.metadata.reflect(some_engine)
 273
 274    class User(Base):
 275        __table__ = metadata.tables['user']
 276
 277    class Address(Base):
 278        __table__ = metadata.tables['address']
 279
 280Some configuration schemes may find it more appropriate to use ``__table__``, 
 281such as those which already take advantage of the data-driven nature of 
 282:class:`.Table` to customize and/or automate schema definition. 
 283
 284Note that when the ``__table__`` approach is used, the object is immediately
 285usable as a plain :class:`.Table` within the class declaration body itself,
 286as a Python class is only another syntactical block.  Below this is illustrated
 287by using the ``id`` column in the ``primaryjoin`` condition of a :func:`.relationship`::
 288
 289    class MyClass(Base):
 290        __table__ = Table('my_table', Base.metadata,
 291            Column('id', Integer, primary_key=True),
 292            Column('name', String(50))
 293        )
 294
 295        widgets = relationship(Widget, 
 296                    primaryjoin=Widget.myclass_id==__table__.c.id)
 297
 298Similarly, mapped attributes which refer to ``__table__`` can be placed inline, 
 299as below where we assign the ``name`` column to the attribute ``_name``, generating
 300a synonym for ``name``::
 301
 302    from sqlalchemy.ext.declarative import synonym_for
 303    
 304    class MyClass(Base):
 305        __table__ = Table('my_table', Base.metadata,
 306            Column('id', Integer, primary_key=True),
 307            Column('name', String(50))
 308        )
 309
 310        _name = __table__.c.name
 311
 312        @synonym_for("_name")
 313        def name(self):
 314            return "Name: %s" % _name
 315
 316Using Reflection with Declarative
 317=================================
 318
 319It's easy to set up a :class:`.Table` that uses ``autoload=True``
 320in conjunction with a mapped class::
 321
 322    class MyClass(Base):
 323        __table__ = Table('mytable', Base.metadata, 
 324                        autoload=True, autoload_with=some_engine)
 325
 326However, one improvement that can be made here is to not 
 327require the :class:`.Engine` to be available when classes are 
 328being first declared.   To achieve this, use the example
 329described at :ref:`examples_declarative_reflection` to build a 
 330declarative base that sets up mappings only after a special 
 331``prepare(engine)`` step is called::
 332
 333    Base = declarative_base(cls=DeclarativeReflectedBase)
 334
 335    class Foo(Base):
 336        __tablename__ = 'foo'
 337        bars = relationship("Bar")
 338
 339    class Bar(Base):
 340        __tablename__ = 'bar'
 341
 342        # illustrate overriding of "bar.foo_id" to have 
 343        # a foreign key constraint otherwise not
 344        # reflected, such as when using MySQL
 345        foo_id = Column(Integer, ForeignKey('foo.id'))
 346
 347    Base.prepare(e)
 348
 349        
 350Mapper Configuration
 351====================
 352
 353Declarative makes use of the :func:`~.orm.mapper` function internally
 354when it creates the mapping to the declared table.   The options
 355for :func:`~.orm.mapper` are passed directly through via the ``__mapper_args__``
 356class attribute.  As always, arguments which reference locally
 357mapped columns can reference them directly from within the 
 358class declaration::
 359
 360    from datetime import datetime
 361
 362    class Widget(Base):
 363        __tablename__ = 'widgets'
 364
 365        id = Column(Integer, primary_key=True)
 366        timestamp = Column(DateTime, nullable=False)
 367
 368        __mapper_args__ = {
 369                        'version_id_col': timestamp,
 370                        'version_id_generator': lambda v:datetime.now()
 371                    }
 372
 373.. _declarative_inheritance:
 374
 375Inheritance Configuration
 376=========================
 377
 378Declarative supports all three forms of inheritance as intuitively
 379as possible.  The ``inherits`` mapper keyword argument is not needed
 380as declarative will determine this from the class itself.   The various
 381"polymorphic" keyword arguments are specified using ``__mapper_args__``.
 382
 383Joined Table Inheritance
 384~~~~~~~~~~~~~~~~~~~~~~~~
 385
 386Joined table inheritance is defined as a subclass that defines its own 
 387table::
 388
 389    class Person(Base):
 390        __tablename__ = 'people'
 391        id = Column(Integer, primary_key=True)
 392        discriminator = Column('type', String(50))
 393        __mapper_args__ = {'polymorphic_on': discriminator}
 394
 395    class Engineer(Person):
 396        __tablename__ = 'engineers'
 397        __mapper_args__ = {'polymorphic_identity': 'engineer'}
 398        id = Column(Integer, ForeignKey('people.id'), primary_key=True)
 399        primary_language = Column(String(50))
 400
 401Note that above, the ``Engineer.id`` attribute, since it shares the
 402same attribute name as the ``Person.id`` attribute, will in fact
 403represent the ``people.id`` and ``engineers.id`` columns together, and
 404will render inside a query as ``"people.id"``. 
 405To provide the ``Engineer`` class with an attribute that represents
 406only the ``engineers.id`` column, give it a different attribute name::
 407
 408    class Engineer(Person):
 409        __tablename__ = 'engineers'
 410        __mapper_args__ = {'polymorphic_identity': 'engineer'}
 411        engineer_id = Column('id', Integer, ForeignKey('people.id'),
 412                                                    primary_key=True)
 413        primary_language = Column(String(50))
 414
 415Single Table Inheritance
 416~~~~~~~~~~~~~~~~~~~~~~~~
 417
 418Single table inheritance is defined as a subclass that does not have
 419its own table; you just leave out the ``__table__`` and ``__tablename__``
 420attributes:: 
 421
 422    class Person(Base):
 423        __tablename__ = 'people'
 424        id = Column(Integer, primary_key=True)
 425        discriminator = Column('type', String(50))
 426        __mapper_args__ = {'polymorphic_on': discriminator}
 427
 428    class Engineer(Person):
 429        __mapper_args__ = {'polymorphic_identity': 'engineer'}
 430        primary_language = Column(String(50))
 431
 432When the above mappers are configured, the ``Person`` class is mapped
 433to the ``people`` table *before* the ``primary_language`` column is
 434defined, and this column will not be included in its own mapping.
 435When ``Engineer`` then defines the ``primary_language`` column, the
 436column is added to the ``people`` table so that it is included in the
 437mapping for ``Engineer`` and is also part of the table's full set of
 438columns.  Columns which are not mapped to ``Person`` are also excluded
 439from any other single or joined inheriting classes using the
 440``exclude_properties`` mapper argument.  Below, ``Manager`` will have
 441all the attributes of ``Person`` and ``Manager`` but *not* the
 442``primary_language`` attribute of ``Engineer``::
 443
 444    class Manager(Person):
 445        __mapper_args__ = {'polymorphic_identity': 'manager'}
 446        golf_swing = Column(String(50))
 447
 448The attribute exclusion logic is provided by the
 449``exclude_properties`` mapper argument, and declarative's default
 450behavior can be disabled by passing an explicit ``exclude_properties``
 451collection (empty or otherwise) to the ``__mapper_args__``.
 452
 453Concrete Table Inheritance
 454~~~~~~~~~~~~~~~~~~~~~~~~~~
 455
 456Concrete is defined as a subclass which has its own table and sets the
 457``concrete`` keyword argument to ``True``::
 458
 459    class Person(Base):
 460        __tablename__ = 'people'
 461        id = Column(Integer, primary_key=True)
 462        name = Column(String(50))
 463
 464    class Engineer(Person):
 465        __tablename__ = 'engineers'
 466        __mapper_args__ = {'concrete':True}
 467        id = Column(Integer, primary_key=True)
 468        primary_language = Column(String(50))
 469        name = Column(String(50))
 470
 471Usage of an abstract base class is a little less straightforward as it
 472requires usage of :func:`~sqlalchemy.orm.util.polymorphic_union`,
 473which needs to be created with the :class:`.Table` objects
 474before the class is built::
 475
 476    engineers = Table('engineers', Base.metadata,
 477                    Column('id', Integer, primary_key=True),
 478                    Column('name', String(50)),
 479                    Column('primary_language', String(50))
 480                )
 481    managers = Table('managers', Base.metadata,
 482                    Column('id', Integer, primary_key=True),
 483                    Column('name', String(50)),
 484                    Column('golf_swing', String(50))
 485                )
 486
 487    punion = polymorphic_union({
 488        'engineer':engineers,
 489        'manager':managers
 490    }, 'type', 'punion')
 491
 492    class Person(Base):
 493        __table__ = punion
 494        __mapper_args__ = {'polymorphic_on':punion.c.type}
 495
 496    class Engineer(Person):
 497        __table__ = engineers
 498        __mapper_args__ = {'polymorphic_identity':'engineer', 'concrete':True}
 499
 500    class Manager(Person):
 501        __table__ = managers
 502        __mapper_args__ = {'polymorphic_identity':'manager', 'concrete':True}
 503
 504.. _declarative_concrete_helpers:
 505
 506Using the Concrete Helpers
 507^^^^^^^^^^^^^^^^^^^^^^^^^^^
 508
 509Helper classes provides a simpler pattern for concrete inheritance.
 510With these objects, the ``__declare_last__`` helper is used to configure the "polymorphic"
 511loader for the mapper after all subclasses have been declared.
 512
 513.. versionadded:: 0.7.3
 514
 515An abstract base can be declared using the :class:`.AbstractConcreteBase` class::
 516
 517    from sqlalchemy.ext.declarative import AbstractConcreteBase
 518    
 519    class Employee(AbstractConcreteBase, Base):
 520        pass
 521
 522To have a concrete ``employee`` table, use :class:`.ConcreteBase` instead::
 523
 524    from sqlalchemy.ext.declarative import ConcreteBase
 525    
 526    class Employee(ConcreteBase, Base):
 527        __tablename__ = 'employee'
 528        employee_id = Column(Integer, primary_key=True)
 529        name = Column(String(50))
 530        __mapper_args__ = {
 531                        'polymorphic_identity':'employee', 
 532                        'concrete':True}
 533    
 534
 535Either ``Employee`` base can be used in the normal fashion::
 536
 537    class Manager(Employee):
 538        __tablename__ = 'manager'
 539        employee_id = Column(Integer, primary_key=True)
 540        name = Column(String(50))
 541        manager_data = Column(String(40))
 542        __mapper_args__ = {
 543                        'polymorphic_identity':'manager', 
 544                        'concrete':True}
 545
 546    class Engineer(Employee):
 547        __tablename__ = 'engineer'
 548        employee_id = Column(Integer, primary_key=True)
 549        name = Column(String(50))
 550        engineer_info = Column(String(40))
 551        __mapper_args__ = {'polymorphic_identity':'engineer', 
 552                        'concrete':True}
 553
 554
 555.. _declarative_mixins:
 556
 557Mixin and Custom Base Classes
 558==============================
 559
 560A common need when using :mod:`~sqlalchemy.ext.declarative` is to
 561share some functionality, such as a set of common columns, some common
 562table options, or other mapped properties, across many
 563classes.  The standard Python idioms for this is to have the classes
 564inherit from a base which includes these common features.
 565
 566When using :mod:`~sqlalchemy.ext.declarative`, this idiom is allowed
 567via the usage of a custom declarative base class, as well as a "mixin" class
 568which is inherited from in addition to the primary base.  Declarative
 569includes several helper features to make this work in terms of how
 570mappings are declared.   An example of some commonly mixed-in
 571idioms is below::
 572
 573    from sqlalchemy.ext.declarative import declared_attr
 574    
 575    class MyMixin(object):
 576
 577        @declared_attr
 578        def __tablename__(cls):
 579            return cls.__name__.lower()
 580
 581        __table_args__ = {'mysql_engine': 'InnoDB'}
 582        __mapper_args__= {'always_refresh': True}
 583
 584        id =  Column(Integer, primary_key=True)
 585
 586    class MyModel(MyMixin, Base):
 587        name = Column(String(1000))
 588
 589Where above, the class ``MyModel`` will contain an "id" column
 590as the primary key, a ``__tablename__`` attribute that derives
 591from the name of the class itself, as well as ``__table_args__`` 
 592and ``__mapper_args__`` defined by the ``MyMixin`` mixin class.
 593
 594There's no fixed convention over whether ``MyMixin`` precedes 
 595``Base`` or not.  Normal Python method resolution rules apply, and 
 596the above example would work just as well with::
 597
 598    class MyModel(Base, MyMixin):
 599        name = Column(String(1000))
 600
 601This works because ``Base`` here doesn't define any of the 
 602variables that ``MyMixin`` defines, i.e. ``__tablename__``, 
 603``__table_args__``, ``id``, etc.   If the ``Base`` did define 
 604an attribute of the same name, the class placed first in the 
 605inherits list would determine which attribute is used on the 
 606newly defined class.
 607
 608Augmenting the Base
 609~~~~~~~~~~~~~~~~~~~
 610
 611In addition to using a pure mixin, most of the techniques in this 
 612section can also be applied to the base class itself, for patterns that
 613should apply to all classes derived from a particular base.  This 
 614is achieved using the ``cls`` argument of the :func:`.declarative_base` function::
 615
 616    from sqlalchemy.ext.declarative import declared_attr
 617
 618    class Base(object):
 619        @declared_attr
 620        def __tablename__(cls):
 621            return cls.__name__.lower()
 622            
 623        __table_args__ = {'mysql_engine': 'InnoDB'}
 624
 625        id =  Column(Integer, primary_key=True)
 626
 627    from sqlalchemy.ext.declarative import declarative_base
 628    
 629    Base = declarative_base(cls=Base)
 630
 631    class MyModel(Base):
 632        name = Column(String(1000))
 633
 634Where above, ``MyModel`` and all other classes that derive from ``Base`` will have 
 635a table name derived from the class name, an ``id`` primary key column, as well as 
 636the "InnoDB" engine for MySQL.
 637
 638Mixing in Columns
 639~~~~~~~~~~~~~~~~~
 640
 641The most basic way to specify a column on a mixin is by simple 
 642declaration::
 643
 644    class TimestampMixin(object):
 645        created_at = Column(DateTime, default=func.now())
 646
 647    class MyModel(TimestampMixin, Base):
 648        __tablename__ = 'test'
 649
 650        id =  Column(Integer, primary_key=True)
 651        name = Column(String(1000))
 652
 653Where above, all declarative classes that include ``TimestampMixin``
 654will also have a column ``created_at`` that applies a timestamp to 
 655all row insertions.
 656
 657Those familiar with the SQLAlchemy expression language know that 
 658the object identity of clause elements defines their role in a schema.
 659Two ``Table`` objects ``a`` and ``b`` may both have a column called 
 660``id``, but the way these are differentiated is that ``a.c.id`` 
 661and ``b.c.id`` are two distinct Python objects, referencing their
 662parent tables ``a`` and ``b`` respectively.
 663
 664In the case of the mixin column, it seems that only one
 665:class:`.Column` object is explicitly created, yet the ultimate 
 666``created_at`` column above must exist as a distinct Python object
 667for each separate destination class.  To accomplish this, the declarative
 668extension creates a **copy** of each :class:`.Column` object encountered on 
 669a class that is detected as a mixin.
 670
 671This copy mechanism is limited to simple columns that have no foreign
 672keys, as a :class:`.ForeignKey` itself contains references to columns
 673which can't be properly recreated at this level.  For columns that 
 674have foreign keys, as well as for the variety of mapper-level constructs
 675that require destination-explicit context, the
 676:func:`~.declared_attr` decorator is provided so that
 677patterns common to many classes can be defined as callables::
 678
 679    from sqlalchemy.ext.declarative import declared_attr
 680
 681    class ReferenceAddressMixin(object):
 682        @declared_attr
 683        def address_id(cls):
 684            return Column(Integer, ForeignKey('address.id'))
 685
 686    class User(ReferenceAddressMixin, Base):
 687        __tablename__ = 'user'
 688        id = Column(Integer, primary_key=True)
 689
 690Where above, the ``address_id`` class-level callable is executed at the 
 691point at which the ``User`` class is constructed, and the declarative
 692extension can use the resulting :class:`.Column` object as returned by
 693the method without the need to copy it.
 694
 695.. versionchanged:: > 0.6.5
 696    Rename 0.6.5 ``sqlalchemy.util.classproperty`` into :func:`~.declared_attr`.
 697
 698Columns generated by :func:`~.declared_attr` can also be
 699referenced by ``__mapper_args__`` to a limited degree, currently 
 700by ``polymorphic_on`` and ``version_id_col``, by specifying the 
 701classdecorator itself into the dictionary - the declarative extension
 702will resolve them at class construction time::
 703
 704    class MyMixin:
 705        @declared_attr
 706        def type_(cls):
 707            return Column(String(50))
 708
 709        __mapper_args__= {'polymorphic_on':type_}
 710
 711    class MyModel(MyMixin, Base):
 712        __tablename__='test'
 713        id =  Column(Integer, primary_key=True)
 714
 715Mixing in Relationships
 716~~~~~~~~~~~~~~~~~~~~~~~
 717
 718Relationships created by :func:`~sqlalchemy.orm.relationship` are provided
 719with declarative mixin classes exclusively using the
 720:func:`.declared_attr` approach, eliminating any ambiguity
 721which could arise when copying a relationship and its possibly column-bound
 722contents. Below is an example which combines a foreign key column and a
 723relationship so that two classes ``Foo`` and ``Bar`` can both be configured to
 724reference a common target class via many-to-one::
 725
 726    class RefTargetMixin(object):
 727        @declared_attr
 728        def target_id(cls):
 729            return Column('target_id', ForeignKey('target.id'))
 730
 731        @declared_attr
 732        def target(cls):
 733            return relationship("Target")
 734
 735    class Foo(RefTargetMixin, Base):
 736        __tablename__ = 'foo'
 737        id = Column(Integer, primary_key=True)
 738
 739    class Bar(RefTargetMixin, Base):
 740        __tablename__ = 'bar'
 741        id = Column(Integer, primary_key=True)
 742
 743    class Target(Base):
 744        __tablename__ = 'target'
 745        id = Column(Integer, primary_key=True)
 746
 747:func:`~sqlalchemy.orm.relationship` definitions which require explicit
 748primaryjoin, order_by etc. expressions should use the string forms 
 749for these arguments, so that they are evaluated as late as possible.
 750To reference the mixin class in these expressions, use the given ``cls``
 751to get it's name::
 752
 753    class RefTargetMixin(object):
 754        @declared_attr
 755        def target_id(cls):
 756            return Column('target_id', ForeignKey('target.id'))
 757
 758        @declared_attr
 759        def target(cls):
 760            return relationship("Target",
 761                primaryjoin="Target.id==%s.target_id" % cls.__name__
 762            )
 763
 764Mixing in deferred(), column_property(), etc.
 765~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 766
 767Like :func:`~sqlalchemy.orm.relationship`, all
 768:class:`~sqlalchemy.orm.interfaces.MapperProperty` subclasses such as
 769:func:`~sqlalchemy.orm.deferred`, :func:`~sqlalchemy.orm.column_property`,
 770etc. ultimately involve references to columns, and therefore, when 
 771used with declarative mixins, have the :func:`.declared_attr` 
 772requirement so that no reliance on copying is needed::
 773
 774    class SomethingMixin(object):
 775
 776        @declared_attr
 777        def dprop(cls):
 778            return deferred(Column(Integer))
 779
 780    class Something(SomethingMixin, Base):
 781        __tablename__ = "something"
 782
 783
 784Controlling table inheritance with mixins
 785~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 786
 787The ``__tablename__`` attribute in conjunction with the hierarchy of
 788classes involved in a declarative mixin scenario controls what type of 
 789table inheritance, if any,
 790is configured by the declarative extension.
 791
 792If the ``__tablename__`` is computed by a mixin, you may need to
 793control which classes get the computed attribute in order to get the
 794type of table inheritance you require.
 795
 796For example, if you had a mixin that computes ``__tablename__`` but
 797where you wanted to use that mixin in a single table inheritance
 798hierarchy, you can explicitly specify ``__tablename__`` as ``None`` to
 799indicate that the class should not have a table mapped::
 800
 801    from sqlalchemy.ext.declarative import declared_attr
 802
 803    class Tablename:
 804        @declared_attr
 805        def __tablename__(cls):
 806            return cls.__name__.lower()
 807
 808    class Person(Tablename, Base):
 809        id = Column(Integer, primary_key=True)
 810        discriminator = Column('type', String(50))
 811        __mapper_args__ = {'polymorphic_on': discriminator}
 812
 813    class Engineer(Person):
 814        __tablename__ = None
 815        __mapper_args__ = {'polymorphic_identity': 'engineer'}
 816        primary_language = Column(String(50))
 817
 818Alternatively, you can make the mixin intelligent enough to only
 819return a ``__tablename__`` in the event that no table is already
 820mapped in the inheritance hierarchy. To help with this, a
 821:func:`~sqlalchemy.ext.declarative.has_inherited_table` helper
 822function is provided that returns ``True`` if a parent class already
 823has a mapped table. 
 824
 825As an example, here's a mixin that will only allow single table
 826inheritance::
 827
 828    from sqlalchemy.ext.declarative import declared_attr
 829    from sqlalchemy.ext.declarative import has_inherited_table
 830
 831    class Tablename(object):
 832        @declared_attr
 833        def __tablename__(cls):
 834            if has_inherited_table(cls):
 835                return None
 836            return cls.__name__.lower()
 837
 838    class Person(Tablename, Base):
 839        id = Column(Integer, primary_key=True)
 840        discriminator = Column('type', String(50))
 841        __mapper_args__ = {'polymorphic_on': discriminator}
 842
 843    class Engineer(Person):
 844        primary_language = Column(String(50))
 845        __mapper_args__ = {'polymorphic_identity': 'engineer'}
 846
 847If you want to use a similar pattern with a mix of single and joined
 848table inheritance, you would need a slightly different mixin and use
 849it on any joined table child classes in addition to their parent
 850classes::
 851
 852    from sqlalchemy.ext.declarative import declared_attr
 853    from sqlalchemy.ext.declarative import has_inherited_table
 854
 855    class Tablename(object):
 856        @declared_attr
 857        def __tablename__(cls):
 858            if (has_inherited_table(cls) and
 859                Tablename not in cls.__bases__):
 860                return None
 861            return cls.__name__.lower()
 862
 863    class Person(Tablename, Base):
 864        id = Column(Integer, primary_key=True)
 865        discriminator = Column('type', String(50))
 866        __mapper_args__ = {'polymorphic_on': discriminator}
 867
 868    # This is single table inheritance
 869    class Engineer(Person):
 870        primary_language = Column(String(50))
 871        __mapper_args__ = {'polymorphic_identity': 'engineer'}
 872
 873    # This is joined table inheritance
 874    class Manager(Tablename, Person):
 875        id = Column(Integer, ForeignKey('person.id'), primary_key=True)
 876        preferred_recreation = Column(String(50))
 877        __mapper_args__ = {'polymorphic_identity': 'engineer'}
 878
 879Combining Table/Mapper Arguments from Multiple Mixins
 880~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 881
 882In the case of ``__table_args__`` or ``__mapper_args__``
 883specified with declarative mixins, you may want to combine
 884some parameters from several mixins with those you wish to
 885define on the class iteself. The
 886:func:`.declared_attr` decorator can be used
 887here to create user-defined collation routines that pull
 888from multiple collections::
 889
 890    from sqlalchemy.ext.declarative import declared_attr
 891
 892    class MySQLSettings(object):
 893        __table_args__ = {'mysql_engine':'InnoDB'}
 894
 895    class MyOtherMixin(object):
 896        __table_args__ = {'info':'foo'}
 897
 898    class MyModel(MySQLSettings, MyOtherMixin, Base):
 899        __tablename__='my_model'
 900
 901        @declared_attr
 902        def __table_args__(cls):
 903            args = dict()
 904            args.update(MySQLSettings.__table_args__)
 905            args.update(MyOtherMixin.__table_args__)
 906            return args
 907
 908        id =  Column(Integer, primary_key=True)
 909
 910Creating Indexes with Mixins
 911~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 912
 913To define a named, potentially multicolumn :class:`.Index` that applies to all 
 914tables derived from a mixin, use the "inline" form of :class:`.Index` and establish
 915it as part of ``__table_args__``::
 916
 917    class MyMixin(object):
 918        a =  Column(Integer)
 919        b =  Column(Integer)
 920
 921        @declared_attr
 922        def __table_args__(cls):
 923            return (Index('test_idx_%s' % cls.__tablename__, 'a', 'b'),)
 924
 925    class MyModel(MyMixin, Base):
 926        __tablename__ = 'atable'
 927        c =  Column(Integer,primary_key=True)
 928
 929Special Directives
 930==================
 931
 932``__declare_last__()``
 933~~~~~~~~~~~~~~~~~~~~~~
 934
 935The ``__declare_last__()`` hook allows definition of 
 936a class level function that is automatically called by the :meth:`.MapperEvents.after_configured`
 937event, which occurs after mappings are assumed to be completed and the 'configure' step
 938has finished::
 939
 940    class MyClass(Base):
 941        @classmethod
 942        def __declare_last__(cls):
 943            ""
 944            # do something with mappings
 945
 946.. versionadded:: 0.7.3
 947
 948.. _declarative_abstract:
 949
 950``__abstract__``
 951~~~~~~~~~~~~~~~~~~~
 952
 953``__abstract__`` causes declarative to skip the production
 954of a table or mapper for the class entirely.  A class can be added within a hierarchy
 955in the same way as mixin (see :ref:`declarative_mixins`), allowing subclasses to extend
 956just from the special class::
 957
 958    class SomeAbstractBase(Base):
 959        __abstract__ = True
 960        
 961        def some_helpful_method(self):
 962            ""
 963            
 964        @declared_attr
 965        def __mapper_args__(cls):
 966            return {"helpful mapper arguments":True}
 967
 968    class MyMappedClass(SomeAbstractBase):
 969        ""
 970        
 971One possible use of ``__abstract__`` is to use a distinct :class:`.MetaData` for different
 972bases::
 973
 974    Base = declarative_base()
 975
 976    class DefaultBase(Base):
 977        __abstract__ = True
 978        metadata = MetaData()
 979
 980    class OtherBase(Base):
 981        __abstract__ = True
 982        metadata = MetaData()
 983
 984Above, classes which inherit from ``DefaultBase`` will use one :class:`.MetaData` as the 
 985registry of tables, and those which inherit from ``OtherBase`` will use a different one.  
 986The tables themselves can then be created perhaps within distinct databases::
 987
 988    DefaultBase.metadata.create_all(some_engine)
 989    OtherBase.metadata_create_all(some_other_engine)
 990
 991.. versionadded:: 0.7.3
 992
 993Class Constructor
 994=================
 995
 996As a convenience feature, the :func:`declarative_base` sets a default
 997constructor on classes which takes keyword arguments, and assigns them
 998to the named attributes::
 999
1000    e = Engineer(primary_language='python')
1001
1002Sessions
1003========
1004
1005Note that ``declarative`` does nothing special with sessions, and is
1006only intended as an easier way to configure mappers and
1007:class:`~sqlalchemy.schema.Table` objects.  A typical application
1008setup using :func:`~sqlalchemy.orm.scoped_session` might look like::
1009
1010    engine = create_engine('postgresql://scott:tiger@localhost/test')
1011    Session = scoped_session(sessionmaker(autocommit=False,
1012                                          autoflush=False,
1013                                          bind=engine))
1014    Base = declarative_base()
1015
1016Mapped instances then make usage of
1017:class:`~sqlalchemy.orm.session.Session` in the usual way. 
1018
1019"""
1020
1021from sqlalchemy.schema import Table, Column, MetaData, _get_table_key
1022from sqlalchemy.orm import synonym as _orm_synonym, mapper,\
1023                                comparable_property, class_mapper
1024from sqlalchemy.orm.interfaces import MapperProperty
1025from sqlalchemy.orm.properties import RelationshipProperty, ColumnProperty, CompositeProperty
1026from sqlalchemy.orm.util import _is_mapped_class
1027from sqlalchemy import util, exc
1028from sqlalchemy.sql import util as sql_util, expression
1029from sqlalchemy import event
1030from sqlalchemy.orm.util import polymorphic_union, _mapper_or_none
1031
1032
1033__all__ = 'declarative_base', 'synonym_for', \
1034            'comparable_using', 'instrument_declarative'
1035
1036def instrument_declarative(cls, registry, metadata):
1037    """Given a class, configure the class declaratively,
1038    using the given registry, which can be any dictionary, and
1039    MetaData object. 
1040
1041    """
1042    if '_decl_class_registry' in cls.__dict__:
1043        raise exc.InvalidRequestError(
1044                            "Class %r already has been "
1045                            "instrumented declaratively" % cls)
1046    cls._decl_class_registry = registry
1047    cls.metadata = metadata
1048    _as_declarative(cls, cls.__name__, cls.__dict__)
1049
1050def has_inherited_table(cls):
1051    """Given a class, return True if any of the classes it inherits from has a
1052    mapped table, otherwise return False.
1053    """
1054    for class_ in cls.__mro__:
1055        if getattr(class_,'__table__',None) is not None:
1056            return True
1057    return False
1058
1059def _as_declarative(cls, classname, dict_):
1060
1061    # dict_ will be a dictproxy, which we can't write to, and we need to!
1062    dict_ = dict(dict_)
1063
1064    column_copies = {}
1065    potential_columns = {}
1066
1067    mapper_args = {}
1068    table_args = inherited_table_args = None
1069    tablename = None
1070    parent_columns = ()
1071
1072    declarative_props = (declared_attr, util.classproperty)
1073
1074    for base in cls.__mro__:
1075        _is_declarative_inherits = hasattr(base, '_decl_class_registry')
1076
1077        if '__declare_last__' in base.__dict__:
1078            @event.listens_for(mapper, "after_configured")
1079            def go():
1080                cls.__declare_last__()
1081        if '__abstract__' in base.__dict__:
1082            if (base is cls or 
1083                (base in cls.__bases__ and not _is_declarative_inherits)
1084            ):
1085                return
1086
1087        class_mapped = _is_mapped_class(base)
1088        if class_mapped:
1089            parent_columns = base.__table__.c.keys()
1090
1091        for name,obj in vars(base).items():
1092            if name == '__mapper_args__':
1093                if not mapper_args and (
1094                                        not class_mapped or 
1095                                        isinstance(obj, declarative_props)
1096                                    ):
1097                    mapper_args = cls.__mapper_args__
1098            elif name == '__tablename__':
1099                if not tablename and (
1100                                        not class_mapped or 
1101                                        isinstance(obj, declarative_props)
1102                                    ):
1103                    tablename = cls.__tablename__
1104            elif name == '__table_args__':
1105                if not table_args and (
1106                                        not class_mapped or 
1107                                        isinstance(obj, declarative_props)
1108                                    ):
1109                    table_args = cls.__table_args__
1110                    if not isinstance(table_args, (tuple, dict, type(None))):
1111                        raise exc.ArgumentError(
1112                                "__table_args__ value must be a tuple, "
1113                                "dict, or None")
1114                    if base is not cls:
1115                        inherited_table_args = True
1116            elif class_mapped:
1117                if isinstance(obj, declarative_props):
1118                    util.warn("Regular (i.e. not __special__) "
1119                            "attribute '%s.%s' uses @declared_attr, "
1120                            "but owning class %s is mapped - "
1121                            "not applying to subclass %s." 
1122                            % (base.__name__, name, base, cls))
1123                continue
1124            elif base is not cls:
1125                # we're a mixin.
1126                if isinstance(obj, Column):
1127                    if obj.foreign_keys:
1128                        raise exc.InvalidRequestError(
1129                        "Columns with foreign keys to other columns "
1130                        "must be declared as @declared_attr callables "
1131                        "on declarative mixin classes. ")
1132                    if name not in dict_ and not (
1133                            '__table__' in dict_ and 
1134                            (obj.name or name) in dict_['__table__'].c
1135                            ) and name not in potential_columns:
1136                        potential_columns[name] = \
1137                                column_copies[obj] = \
1138                                obj.copy()
1139                        column_copies[obj]._creation_order = \
1140                                obj._creation_order
1141                elif isinstance(obj, MapperProperty):
1142                    raise exc.InvalidRequestError(
1143                        "Mapper properties (i.e. deferred,"
1144                        "column_property(), relationship(), etc.) must "
1145                        "be declared as @declared_attr callables "
1146                        "on declarative mixin classes.")
1147                elif isinstance(obj, declarative_props):
1148                    dict_[name] = ret = \
1149                            column_copies[obj] = getattr(cls, name)
1150                    if isinstance(ret, (Column, MapperProperty)) and \
1151                        ret.doc is None:
1152                        ret.doc = obj.__doc__
1153
1154    # apply inherited columns as we should
1155    for k, v in potential_columns.items():
1156        if tablename or (v.name or k) not in parent_columns:
1157            dict_[k] = v
1158
1159    if inherited_table_args and not tablename:
1160        table_args = None
1161
1162    # make sure that column copies are used rather 
1163    # than the original columns from any mixins
1164    for k in ('version_id_col', 'polymorphic_on',):
1165        if k in mapper_args:
1166            v = mapper_args[k]
1167            mapper_args[k] = column_copies.get(v,v)
1168
1169    if classname in cls._decl_class_registry:
1170        util.warn("The classname %r is already in the registry of this"
1171                  " declarative base, mapped to %r" % (
1172                classname,
1173                cls._decl_class_registry[classname]
1174                ))
1175    cls._decl_class_registry[classname] = cls
1176    our_stuff = util.OrderedDict()
1177
1178    for k in dict_:
1179        value = dict_[k]
1180        if isinstance(value, declarative_props):
1181            value = getattr(cls, k)
1182
1183        if (isinstance(value, tuple) and len(value) == 1 and
1184            isinstance(value[0], (Column, MapperProperty))):
1185            util.warn("Ignoring declarative-like tuple value of attribute "
1186                      "%s: possibly a copy-and-paste error with a comma "
1187                      "left at the end of the line?" % k)
1188            continue
1189        if not isinstance(value, (Column, MapperProperty)):
1190            continue
1191        if k == 'metadata':
1192            raise exc.InvalidRequestError(
1193                "Attribute name 'metadata' is reserved "
1194                "for the MetaData instance when using a "
1195                "declarative base class."
1196            )
1197        prop = _deferred_relationship(cls, value)
1198        our_stuff[k] = prop
1199
1200    # set up attributes in the order they were created
1201    our_stuff.sort(key=lambda key: our_stuff[key]._creation_order)
1202
1203    # extract columns from the class dict
1204    cols = set()
1205    for key, c in our_stuff.iteritems():
1206        if isinstance(c, (ColumnProperty, CompositeProperty)):
1207            for col in c.columns:
1208                if isinstance(col, Column) and \
1209                    col.table is None:
1210                    _undefer_column_name(key, col)
1211                    cols.add(col)
1212        elif isinstance(c, Column):
1213            _undefer_column_name(key, c)
1214            cols.add(c)
1215            # if the column is the same name as the key, 
1216            # remove it from the explicit properties dict.
1217            # the normal rules for assigning column-based properties
1218            # will take over, including precedence of columns
1219            # in multi-column ColumnProperties.
1220            if key == c.key:
1221                del our_stuff[key]
1222    cols = sorted(cols, key=lambda c:c._creation_order)
1223    table = None
1224
1225    if hasattr(cls, '__table_cls__'):
1226        table_cls = util.unbound_method_to_callable(cls.__table_cls__)
1227    else:
1228        table_cls = Table
1229
1230    if '__table__' not in dict_:
1231        if tablename is not None:
1232
1233            args, table_kw = (), {}
1234            if table_args:
1235                if isinstance(table_args, dict):
1236                    table_kw = table_args
1237                elif isinstance(table_args, tuple):
1238                    if isinstance(table_args[-1], dict):
1239                        args, table_kw = table_args[0:-1], table_args[-1]
1240                    else:
1241                        args = table_args
1242
1243            autoload = dict_.get('__autoload__')
1244            if autoload:
1245                table_kw['autoload'] = True
1246
1247            cls.__table__ = table = table_cls(tablename, cls.metadata,
1248                                          *(tuple(cols) + tuple(args)),
1249                                           **table_kw)
1250    else:
1251        table = cls.__table__
1252        if cols:
1253            for c in cols:
1254                if not table.c.contains_column(c):
1255                    raise exc.ArgumentError(
1256                        "Can't add additional column %r when "
1257                        "specifying __table__" % c.key
1258                    )
1259
1260    if 'inherits' not in mapper_args:
1261        for c in cls.__bases__:
1262            if _is_mapped_class(c):
1263                mapper_args['inherits'] = c
1264                break
1265
1266    if hasattr(cls, '__mapper_cls__'):
1267        mapper_cls = util.unbound_method_to_callable(cls.__mapper_cls__)
1268    else:
1269        mapper_cls = mapper
1270
1271    if table is None and 'inherits' not in mapper_args:
1272        raise exc.InvalidRequestError(
1273            "Class %r does not have a __table__ or __tablename__ "
1274            "specified and does not inherit from an existing "
1275            "table-mapped class." % cls
1276            )
1277
1278    elif 'inherits' in mapper_args and not mapper_args.get('concrete', False):
1279        inherited_mapper = class_mapper(mapper_args['inherits'],
1280                                            compile=False)
1281        inherited_table = inherited_mapper.local_table
1282
1283        if table is None:
1284            # single table inheritance.
1285            # ensure no table args
1286            if table_args:
1287                raise exc.ArgumentError(
1288                    "Can't place __table_args__ on an inherited class "
1289                    "with no table."
1290                    )
1291
1292            # add any columns declared here to the inherited table.
1293            for c in cols:
1294                if c.primary_key:
1295                    raise exc.ArgumentError(
1296                        "Can't place primary key columns on an inherited "
1297                        "class with no table."
1298                        )
1299                if c.name in inherited_table.c:
1300                    raise exc.ArgumentError(
1301                        "Column '%s' on class %s conflicts with "
1302                        "existing column '%s'" % 
1303                        (c, cls, inherited_table.c[c.name])
1304                    )
1305                inherited_table.append_column(c)
1306
1307        # single or joined inheritance
1308        # exclude any cols on the inherited table which are not mapped on the
1309        # parent class, to avoid
1310        # mapping columns specific to sibling/nephew classes
1311        inherited_mapper = class_mapper(mapper_args['inherits'],
1312                                            compile=False)
1313        inherited_table = inherited_mapper.local_table
1314
1315        if 'exclude_properties' not in mapper_args:
1316            mapper_args['exclude_properties'] = exclude_properties = \
1317                set([c.key for c in inherited_table.c
1318                     if c not in inherited_mapper._columntoproperty])
1319            exclude_properties.difference_update([c.key for c in cols])
1320
1321        # look through columns in the current mapper that 
1322        # are keyed to a propname different than the colname
1323        # (if names were the same, we'd have popped it out above,
1324        # in which case the mapper makes this combination).
1325        # See if the superclass has a similar column property.
1326        # If so, join them together.
1327        for k, col in our_stuff.items():
1328            if not isinstance(col, expression.ColumnElement):
1329                continue
1330            if k in inherited_mapper._props:
1331                p = inherited_mapper._props[k]
1332                if isinstance(p, ColumnProperty):
1333                    # note here we place the subclass column
1334                    # first.  See [ticket:1892] for background.
1335                    our_stuff[k] = [col] + p.columns
1336
1337
1338    cls.__mapper__ = mapper_cls(cls, 
1339                                table, 
1340                                properties=our_stuff, 
1341                                **mapper_args)
1342
1343class DeclarativeMeta(type):
1344    def __init__(cls, classname, bases, dict_):
1345        if '_decl_class_registry' in cls.__dict__:
1346            return type.__init__(cls, classname, bases, dict_)
1347        else:
1348            _as_declarative(cls, classname, cls.__dict__)
1349        return type.__init__(cls, classname, bases, dict_)
1350
1351    def __setattr__(cls, key, value):
1352        if '__mapper__' in cls.__dict__:
1353            if isinstance(value, C

Large files files are truncated, but you can click here to view the full file