From: Mike Bayer Date: Sat, 4 Oct 2014 23:10:23 +0000 (-0400) Subject: - move some things that are features to the features X-Git-Tag: rel_1_0_0b1~70^2~46 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c4dee4cbb7aa1a5486a90d3c48ffd8500d3a6dd2;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - move some things that are features to the features --- diff --git a/doc/build/changelog/migration_10.rst b/doc/build/changelog/migration_10.rst index 439ec4c670..b3e5f6f152 100644 --- a/doc/build/changelog/migration_10.rst +++ b/doc/build/changelog/migration_10.rst @@ -25,6 +25,141 @@ potentially backwards-incompatible changes. New Features ============ +.. _feature_3150: + +Improvements to declarative mixins, ``@declared_attr`` and related features +---------------------------------------------------------------------------- + +The declarative system in conjunction with :class:`.declared_attr` has been +overhauled to support new capabilities. + +A function decorated with :class:`.declared_attr` is now called only **after** +any mixin-based column copies are generated. This means the function can +call upon mixin-established columns and will receive a reference to the correct +:class:`.Column` object:: + + class HasFooBar(object): + foobar = Column(Integer) + + @declared_attr + def foobar_prop(cls): + return column_property('foobar: ' + cls.foobar) + + class SomeClass(HasFooBar, Base): + __tablename__ = 'some_table' + id = Column(Integer, primary_key=True) + +Above, ``SomeClass.foobar_prop`` will be invoked against ``SomeClass``, +and ``SomeClass.foobar`` will be the final :class:`.Column` object that is +to be mapped to ``SomeClass``, as opposed to the non-copied object present +directly on ``HasFooBar``, even though the columns aren't mapped yet. + +The :class:`.declared_attr` function now **memoizes** the value +that's returned on a per-class basis, so that repeated calls to the same +attribute will return the same value. We can alter the example to illustrate +this:: + + class HasFooBar(object): + @declared_attr + def foobar(cls): + return Column(Integer) + + @declared_attr + def foobar_prop(cls): + return column_property('foobar: ' + cls.foobar) + + class SomeClass(HasFooBar, Base): + __tablename__ = 'some_table' + id = Column(Integer, primary_key=True) + +Previously, ``SomeClass`` would be mapped with one particular copy of +the ``foobar`` column, but the ``foobar_prop`` by calling upon ``foobar`` +a second time would produce a different column. The value of +``SomeClass.foobar`` is now memoized during declarative setup time, so that +even before the attribute is mapped by the mapper, the interim column +value will remain consistent no matter how many times the +:class:`.declared_attr` is called upon. + +The two behaviors above should help considerably with declarative definition +of many types of mapper properties that derive from other attributes, where +the :class:`.declared_attr` function is called upon from other +:class:`.declared_attr` functions locally present before the class is +actually mapped. + +For a pretty slim edge case where one wishes to build a declarative mixin +that establishes distinct columns per subclass, a new modifier +:attr:`.declared_attr.cascading` is added. With this modifier, the +decorated function will be invoked individually for each class in the +mapped inheritance hierarchy. While this is already the behavior for +special attributes such as ``__table_args__`` and ``__mapper_args__``, +for columns and other properties the behavior by default assumes that attribute +is affixed to the base class only, and just inherited from subclasses. +With :attr:`.declared_attr.cascading`, individual behaviors can be +applied:: + + class HasSomeAttribute(object): + @declared_attr.cascading + def some_id(cls): + if has_inherited_table(cls): + return Column(ForeignKey('myclass.id'), primary_key=True) + else: + return Column(Integer, primary_key=True) + + return Column('id', Integer, primary_key=True) + + class MyClass(HasSomeAttribute, Base): + "" + # ... + + class MySubClass(MyClass): + "" + # ... + +.. seealso:: + + :ref:`mixin_inheritance_columns` + +Finally, the :class:`.AbstractConcreteBase` class has been reworked +so that a relationship or other mapper property can be set up inline +on the abstract base:: + + from sqlalchemy import Column, Integer, ForeignKey + from sqlalchemy.orm import relationship + from sqlalchemy.ext.declarative import (declarative_base, declared_attr, + AbstractConcreteBase) + + Base = declarative_base() + + class Something(Base): + __tablename__ = u'something' + id = Column(Integer, primary_key=True) + + + class Abstract(AbstractConcreteBase, Base): + id = Column(Integer, primary_key=True) + + @declared_attr + def something_id(cls): + return Column(ForeignKey(Something.id)) + + @declared_attr + def something(cls): + return relationship(Something) + + + class Concrete(Abstract): + __tablename__ = u'cca' + __mapper_args__ = {'polymorphic_identity': 'cca', 'concrete': True} + + +The above mapping will set up a table ``cca`` with both an ``id`` and +a ``something_id`` column, and ``Concrete`` will also have a relationship +``something``. The new feature is that ``Abstract`` will also have an +independently configured relationship ``something`` that builds against +the polymorphic union of the base. + +:ticket:`3150` :ticket:`2670` :ticket:`3149` :ticket:`2952` :ticket:`3050` + .. _feature_3034: Select/Query LIMIT / OFFSET may be specified as an arbitrary SQL expression @@ -50,6 +185,84 @@ wishes to support the new feature should now call upon the ``._limit_clause`` and ``._offset_clause`` attributes to receive the full SQL expression, rather than the integer value. +.. _change_2051: + +New Postgresql Table options +----------------------------- + +Added support for PG table options TABLESPACE, ON COMMIT, +WITH(OUT) OIDS, and INHERITS, when rendering DDL via +the :class:`.Table` construct. + +.. seealso:: + + :ref:`postgresql_table_options` + +:ticket:`2051` + +.. _feature_get_enums: + +New get_enums() method with Postgresql Dialect +---------------------------------------------- + +The :func:`.inspect` method returns a :class:`.PGInspector` object in the +case of Postgresql, which includes a new :meth:`.PGInspector.get_enums` +method that returns information on all available ``ENUM`` types:: + + from sqlalchemy import inspect, create_engine + + engine = create_engine("postgresql+psycopg2://host/dbname") + insp = inspect(engine) + print(insp.get_enums()) + +.. seealso:: + + :meth:`.PGInspector.get_enums` + +.. _feature_2891: + +Postgresql Dialect reflects Materialized Views, Foreign Tables +-------------------------------------------------------------- + +Changes are as follows: + +* the :class:`Table` construct with ``autoload=True`` will now match a name + that exists in the database as a materialized view or foriegn table. + +* :meth:`.Inspector.get_view_names` will return plain and materialized view + names. + +* :meth:`.Inspector.get_table_names` does **not** change for Postgresql, it + continues to return only the names of plain tables. + +* A new method :meth:`.PGInspector.get_foreign_table_names` is added which + will return the names of tables that are specifically marked as "foreign" + in the Postgresql schema tables. + +The change to reflection involves adding ``'m'`` and ``'f'`` to the list +of qualifiers we use when querying ``pg_class.relkind``, but this change +is new in 1.0.0 to avoid any backwards-incompatible surprises for those +running 0.9 in production. + +:ticket:`2891` + +.. _feature_gh134: + +Postgresql FILTER keyword +------------------------- + +The SQL standard FILTER keyword for aggregate functions is now supported +by Postgresql as of 9.4. SQLAlchemy allows this using +:meth:`.FunctionElement.filter`:: + + func.count(1).filter(True) + +.. seealso:: + + :meth:`.FunctionElement.filter` + + :class:`.FunctionFilter` + .. _feature_3184: UniqueConstraint is now part of the Table reflection process @@ -177,7 +390,6 @@ MacBookPro is 31 seconds on 0.9 and 26 seconds on 1.0, the extra time spent setting up very large memory buffers. - .. _feature_3176: New KeyedTuple implementation dramatically faster @@ -363,140 +575,6 @@ Renders:: :ticket:`3177` -.. _feature_3150: - -Improvements to declarative mixins, ``@declared_attr`` and related features ----------------------------------------------------------------------------- - -The declarative system in conjunction with :class:`.declared_attr` has been -overhauled to support new capabilities. - -A function decorated with :class:`.declared_attr` is now called only **after** -any mixin-based column copies are generated. This means the function can -call upon mixin-established columns and will receive a reference to the correct -:class:`.Column` object:: - - class HasFooBar(object): - foobar = Column(Integer) - - @declared_attr - def foobar_prop(cls): - return column_property('foobar: ' + cls.foobar) - - class SomeClass(HasFooBar, Base): - __tablename__ = 'some_table' - id = Column(Integer, primary_key=True) - -Above, ``SomeClass.foobar_prop`` will be invoked against ``SomeClass``, -and ``SomeClass.foobar`` will be the final :class:`.Column` object that is -to be mapped to ``SomeClass``, as opposed to the non-copied object present -directly on ``HasFooBar``, even though the columns aren't mapped yet. - -The :class:`.declared_attr` function now **memoizes** the value -that's returned on a per-class basis, so that repeated calls to the same -attribute will return the same value. We can alter the example to illustrate -this:: - - class HasFooBar(object): - @declared_attr - def foobar(cls): - return Column(Integer) - - @declared_attr - def foobar_prop(cls): - return column_property('foobar: ' + cls.foobar) - - class SomeClass(HasFooBar, Base): - __tablename__ = 'some_table' - id = Column(Integer, primary_key=True) - -Previously, ``SomeClass`` would be mapped with one particular copy of -the ``foobar`` column, but the ``foobar_prop`` by calling upon ``foobar`` -a second time would produce a different column. The value of -``SomeClass.foobar`` is now memoized during declarative setup time, so that -even before the attribute is mapped by the mapper, the interim column -value will remain consistent no matter how many times the -:class:`.declared_attr` is called upon. - -The two behaviors above should help considerably with declarative definition -of many types of mapper properties that derive from other attributes, where -the :class:`.declared_attr` function is called upon from other -:class:`.declared_attr` functions locally present before the class is -actually mapped. - -For a pretty slim edge case where one wishes to build a declarative mixin -that establishes distinct columns per subclass, a new modifier -:attr:`.declared_attr.cascading` is added. With this modifier, the -decorated function will be invoked individually for each class in the -mapped inheritance hierarchy. While this is already the behavior for -special attributes such as ``__table_args__`` and ``__mapper_args__``, -for columns and other properties the behavior by default assumes that attribute -is affixed to the base class only, and just inherited from subclasses. -With :attr:`.declared_attr.cascading`, individual behaviors can be -applied:: - - class HasSomeAttribute(object): - @declared_attr.cascading - def some_id(cls): - if has_inherited_table(cls): - return Column(ForeignKey('myclass.id'), primary_key=True) - else: - return Column(Integer, primary_key=True) - - return Column('id', Integer, primary_key=True) - - class MyClass(HasSomeAttribute, Base): - "" - # ... - - class MySubClass(MyClass): - "" - # ... - -.. seealso:: - - :ref:`mixin_inheritance_columns` - -Finally, the :class:`.AbstractConcreteBase` class has been reworked -so that a relationship or other mapper property can be set up inline -on the abstract base:: - - from sqlalchemy import Column, Integer, ForeignKey - from sqlalchemy.orm import relationship - from sqlalchemy.ext.declarative import (declarative_base, declared_attr, - AbstractConcreteBase) - - Base = declarative_base() - - class Something(Base): - __tablename__ = u'something' - id = Column(Integer, primary_key=True) - - - class Abstract(AbstractConcreteBase, Base): - id = Column(Integer, primary_key=True) - - @declared_attr - def something_id(cls): - return Column(ForeignKey(Something.id)) - - @declared_attr - def something(cls): - return relationship(Something) - - - class Concrete(Abstract): - __tablename__ = u'cca' - __mapper_args__ = {'polymorphic_identity': 'cca', 'concrete': True} - - -The above mapping will set up a table ``cca`` with both an ``id`` and -a ``something_id`` column, and ``Concrete`` will also have a relationship -``something``. The new feature is that ``Abstract`` will also have an -independently configured relationship ``something`` that builds against -the polymorphic union of the base. - -:ticket:`3150` :ticket:`2670` :ticket:`3149` :ticket:`2952` :ticket:`3050` .. _bug_3188: @@ -1021,84 +1099,6 @@ A :class:`.Table` can be set up for reflection by passing Dialect Changes =============== -.. _change_2051: - -New Postgresql Table options ------------------------------ - -Added support for PG table options TABLESPACE, ON COMMIT, -WITH(OUT) OIDS, and INHERITS, when rendering DDL via -the :class:`.Table` construct. - -.. seealso:: - - :ref:`postgresql_table_options` - -:ticket:`2051` - -.. _feature_get_enums: - -New get_enums() method with Postgresql Dialect ----------------------------------------------- - -The :func:`.inspect` method returns a :class:`.PGInspector` object in the -case of Postgresql, which includes a new :meth:`.PGInspector.get_enums` -method that returns information on all available ``ENUM`` types:: - - from sqlalchemy import inspect, create_engine - - engine = create_engine("postgresql+psycopg2://host/dbname") - insp = inspect(engine) - print(insp.get_enums()) - -.. seealso:: - - :meth:`.PGInspector.get_enums` - -.. _feature_2891: - -Postgresql Dialect reflects Materialized Views, Foreign Tables --------------------------------------------------------------- - -Changes are as follows: - -* the :class:`Table` construct with ``autoload=True`` will now match a name - that exists in the database as a materialized view or foriegn table. - -* :meth:`.Inspector.get_view_names` will return plain and materialized view - names. - -* :meth:`.Inspector.get_table_names` does **not** change for Postgresql, it - continues to return only the names of plain tables. - -* A new method :meth:`.PGInspector.get_foreign_table_names` is added which - will return the names of tables that are specifically marked as "foreign" - in the Postgresql schema tables. - -The change to reflection involves adding ``'m'`` and ``'f'`` to the list -of qualifiers we use when querying ``pg_class.relkind``, but this change -is new in 1.0.0 to avoid any backwards-incompatible surprises for those -running 0.9 in production. - -:ticket:`2891` - -.. _feature_gh134: - -Postgresql FILTER keyword -------------------------- - -The SQL standard FILTER keyword for aggregate functions is now supported -by Postgresql as of 9.4. SQLAlchemy allows this using -:meth:`.FunctionElement.filter`:: - - func.count(1).filter(True) - -.. seealso:: - - :meth:`.FunctionElement.filter` - - :class:`.FunctionFilter` - MySQL internal "no such table" exceptions not passed to event handlers ----------------------------------------------------------------------