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

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