From af61551a112b5cedcaf56470101b8a4f8523573d Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Fri, 19 Jul 2013 22:56:34 -0400 Subject: - Improved the examples in ``examples/generic_associations``, including that ``discriminator_on_association.py`` makes use of single table inheritance do the work with the "discriminator". Also added a true "generic foreign key" example, which works similarly to other popular frameworks in that it uses an open-ended integer to point to any other table, foregoing traditional referential integrity. While we don't recommend this pattern, information wants to be free. Also in 0.8.3. - Added a convenience class decorator :func:`.as_declarative`, is a wrapper for :func:`.declarative_base` which allows an existing base class to be applied using a nifty class-decorated approach. Also in 0.8.3. --- lib/sqlalchemy/ext/declarative/api.py | 40 +++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) (limited to 'lib/sqlalchemy/ext/declarative/api.py') diff --git a/lib/sqlalchemy/ext/declarative/api.py b/lib/sqlalchemy/ext/declarative/api.py index 2f222f682..9cbe32267 100644 --- a/lib/sqlalchemy/ext/declarative/api.py +++ b/lib/sqlalchemy/ext/declarative/api.py @@ -218,6 +218,10 @@ def declarative_base(bind=None, metadata=None, mapper=None, cls=object, compatible callable to use as the meta type of the generated declarative base class. + .. seealso:: + + :func:`.as_declarative` + """ lcl_metadata = metadata or MetaData() if bind: @@ -237,6 +241,42 @@ def declarative_base(bind=None, metadata=None, mapper=None, cls=object, return metaclass(name, bases, class_dict) +def as_declarative(**kw): + """ + Class decorator for :func:`.declarative_base`. + + Provides a syntactical shortcut to the ``cls`` argument + sent to :func:`.declarative_base`, allowing the base class + to be converted in-place to a "declarative" base:: + + from sqlalchemy.ext.declarative import as_declarative + + @as_declarative() + class Base(object) + @declared_attr + def __tablename__(cls): + return cls.__name__.lower() + id = Column(Integer, primary_key=True) + + class MyMappedClass(Base): + # ... + + All keyword arguments passed to :func:`.as_declarative` are passed + along to :func:`.declarative_base`. + + .. versionadded:: 0.8.3 + + .. seealso:: + + :func:`.declarative_base` + + """ + def decorate(cls): + kw['cls'] = cls + kw['name'] = cls.__name__ + return declarative_base(**kw) + + return decorate class ConcreteBase(object): """A helper class for 'concrete' declarative mappings. -- cgit v1.2.1 From e60c16c7e6c2494b623553f41694c1ebde4d65d8 Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Sat, 20 Jul 2013 00:41:10 -0400 Subject: tpo --- lib/sqlalchemy/ext/declarative/api.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'lib/sqlalchemy/ext/declarative/api.py') diff --git a/lib/sqlalchemy/ext/declarative/api.py b/lib/sqlalchemy/ext/declarative/api.py index 9cbe32267..90076fdbb 100644 --- a/lib/sqlalchemy/ext/declarative/api.py +++ b/lib/sqlalchemy/ext/declarative/api.py @@ -249,17 +249,17 @@ def as_declarative(**kw): sent to :func:`.declarative_base`, allowing the base class to be converted in-place to a "declarative" base:: - from sqlalchemy.ext.declarative import as_declarative + from sqlalchemy.ext.declarative import as_declarative - @as_declarative() - class Base(object) - @declared_attr - def __tablename__(cls): - return cls.__name__.lower() - id = Column(Integer, primary_key=True) + @as_declarative() + class Base(object) + @declared_attr + def __tablename__(cls): + return cls.__name__.lower() + id = Column(Integer, primary_key=True) - class MyMappedClass(Base): - # ... + class MyMappedClass(Base): + # ... All keyword arguments passed to :func:`.as_declarative` are passed along to :func:`.declarative_base`. -- cgit v1.2.1 From 59141d360e70d1a762719206e3cb0220b4c53fef Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Wed, 14 Aug 2013 19:58:34 -0400 Subject: - apply an import refactoring to the ORM as well - rework the event system so that event modules load after their targets, dependencies are reversed - create an improved strategy lookup system for the ORM - rework the ORM to have very few import cycles - move out "importlater" to just util.dependency - other tricks to cross-populate modules in as clear a way as possible --- lib/sqlalchemy/ext/declarative/api.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lib/sqlalchemy/ext/declarative/api.py') diff --git a/lib/sqlalchemy/ext/declarative/api.py b/lib/sqlalchemy/ext/declarative/api.py index 90076fdbb..b309a783a 100644 --- a/lib/sqlalchemy/ext/declarative/api.py +++ b/lib/sqlalchemy/ext/declarative/api.py @@ -10,7 +10,8 @@ from ...schema import Table, MetaData from ...orm import synonym as _orm_synonym, mapper,\ comparable_property,\ interfaces -from ...orm.util import polymorphic_union, _mapper_or_none +from ...orm.util import polymorphic_union +from ...orm.base import _mapper_or_none from ... import exc import weakref -- cgit v1.2.1 From a62afc6229969e71c1c8f12f1e47299824eb3884 Mon Sep 17 00:00:00 2001 From: Vraj Mohan Date: Fri, 15 Nov 2013 08:32:07 -0500 Subject: Fix cross references --- lib/sqlalchemy/ext/declarative/api.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'lib/sqlalchemy/ext/declarative/api.py') diff --git a/lib/sqlalchemy/ext/declarative/api.py b/lib/sqlalchemy/ext/declarative/api.py index b309a783a..2f84896aa 100644 --- a/lib/sqlalchemy/ext/declarative/api.py +++ b/lib/sqlalchemy/ext/declarative/api.py @@ -174,16 +174,16 @@ def declarative_base(bind=None, metadata=None, mapper=None, cls=object, of the class. :param bind: An optional - :class:`~sqlalchemy.engine.base.Connectable`, will be assigned - the ``bind`` attribute on the :class:`~sqlalchemy.MetaData` + :class:`~sqlalchemy.engine.Connectable`, will be assigned + the ``bind`` attribute on the :class:`~sqlalchemy.schema.MetaData` instance. :param metadata: - An optional :class:`~sqlalchemy.MetaData` instance. All + An optional :class:`~sqlalchemy.schema.MetaData` instance. All :class:`~sqlalchemy.schema.Table` objects implicitly declared by subclasses of the base will share this MetaData. A MetaData instance will be created if none is provided. The - :class:`~sqlalchemy.MetaData` instance will be available via the + :class:`~sqlalchemy.schema.MetaData` instance will be available via the `metadata` attribute of the generated declarative base class. :param mapper: @@ -286,7 +286,7 @@ class ConcreteBase(object): function automatically, against all tables mapped as a subclass to this class. The function is called via the ``__declare_last__()`` function, which is essentially - a hook for the :func:`.MapperEvents.after_configured` event. + a hook for the :meth:`.after_configured` event. :class:`.ConcreteBase` produces a mapped table for the class itself. Compare to :class:`.AbstractConcreteBase`, @@ -341,7 +341,7 @@ class AbstractConcreteBase(ConcreteBase): function automatically, against all tables mapped as a subclass to this class. The function is called via the ``__declare_last__()`` function, which is essentially - a hook for the :func:`.MapperEvents.after_configured` event. + a hook for the :meth:`.after_configured` event. :class:`.AbstractConcreteBase` does not produce a mapped table for the class itself. Compare to :class:`.ConcreteBase`, @@ -421,7 +421,7 @@ class DeferredReflection(object): Above, ``MyClass`` is not yet mapped. After a series of classes have been defined in the above fashion, all tables can be reflected and mappings created using - :meth:`.DeferredReflection.prepare`:: + :meth:`.prepare`:: engine = create_engine("someengine://...") DeferredReflection.prepare(engine) -- cgit v1.2.1 From e860dcb912165a2dd03e211a503a5f83ddadaa0c Mon Sep 17 00:00:00 2001 From: Hyunjun Kim Date: Thu, 21 Nov 2013 16:36:48 +0900 Subject: Fixed a syntax error in example code. --- lib/sqlalchemy/ext/declarative/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/sqlalchemy/ext/declarative/api.py') diff --git a/lib/sqlalchemy/ext/declarative/api.py b/lib/sqlalchemy/ext/declarative/api.py index 2f84896aa..1cb653a23 100644 --- a/lib/sqlalchemy/ext/declarative/api.py +++ b/lib/sqlalchemy/ext/declarative/api.py @@ -253,7 +253,7 @@ def as_declarative(**kw): from sqlalchemy.ext.declarative import as_declarative @as_declarative() - class Base(object) + class Base(object): @declared_attr def __tablename__(cls): return cls.__name__.lower() -- cgit v1.2.1 From 36e1aa0afdf7e42f88426da4b2e9ee631d16728c Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Tue, 3 Dec 2013 13:46:41 -0500 Subject: - The :class:`.DeferredReflection` class has been enhanced to provide automatic reflection support for the "secondary" table referred to by a :func:`.relationship`. "secondary", when specified either as a string table name, or as a :class:`.Table` object with only a name and :class:`.MetaData` object will also be included in the reflection process when :meth:`.DeferredReflection.prepare` is called. [ticket:2865] - clsregistry._resolver() now uses a stateful _class_resolver() class in order to handle the work of mapping strings to objects. This is to provide for simpler extensibility, namely a ._resolvers collection of ad-hoc name resolution functions; the DeferredReflection class adds its own resolver here in order to handle relationship(secondary) names which generate new Table objects. --- lib/sqlalchemy/ext/declarative/api.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) (limited to 'lib/sqlalchemy/ext/declarative/api.py') diff --git a/lib/sqlalchemy/ext/declarative/api.py b/lib/sqlalchemy/ext/declarative/api.py index 1cb653a23..64bf7fd9f 100644 --- a/lib/sqlalchemy/ext/declarative/api.py +++ b/lib/sqlalchemy/ext/declarative/api.py @@ -9,15 +9,17 @@ from ...schema import Table, MetaData from ...orm import synonym as _orm_synonym, mapper,\ comparable_property,\ - interfaces + interfaces, properties from ...orm.util import polymorphic_union from ...orm.base import _mapper_or_none +from ...util import compat from ... import exc import weakref from .base import _as_declarative, \ _declarative_constructor,\ _MapperConfig, _add_attribute +from .clsregistry import _class_resolver def instrument_declarative(cls, registry, metadata): @@ -465,11 +467,31 @@ class DeferredReflection(object): def prepare(cls, engine): """Reflect all :class:`.Table` objects for all current :class:`.DeferredReflection` subclasses""" + to_map = [m for m in _MapperConfig.configs.values() if issubclass(m.cls, cls)] for thingy in to_map: cls._sa_decl_prepare(thingy.local_table, engine) thingy.map() + mapper = thingy.cls.__mapper__ + metadata = mapper.class_.metadata + for rel in mapper._props.values(): + if isinstance(rel, properties.RelationshipProperty) and \ + rel.secondary is not None: + if isinstance(rel.secondary, Table): + cls._sa_decl_prepare(rel.secondary, engine) + elif isinstance(rel.secondary, _class_resolver): + rel.secondary._resolvers += ( + cls._sa_deferred_table_resolver(engine, metadata), + ) + + @classmethod + def _sa_deferred_table_resolver(cls, engine, metadata): + def _resolve(key): + t1 = Table(key, metadata) + cls._sa_decl_prepare(t1, engine) + return t1 + return _resolve @classmethod def _sa_decl_prepare(cls, local_table, engine): -- cgit v1.2.1 From 31821011271bf2333b69954d53c3c922e39bf225 Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Fri, 3 Jan 2014 21:46:13 -0500 Subject: - Fixed an extremely unlikely memory issue where when using :class:`.DeferredReflection` to define classes pending for reflection, if some subset of those classes were discarded before the :meth:`.DeferredReflection.prepare` method were called to reflect and map the class, a strong reference to the class would remain held within the declarative internals. This internal collection of "classes to map" now uses weak references against the classes themselves. --- lib/sqlalchemy/ext/declarative/api.py | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) (limited to 'lib/sqlalchemy/ext/declarative/api.py') diff --git a/lib/sqlalchemy/ext/declarative/api.py b/lib/sqlalchemy/ext/declarative/api.py index 64bf7fd9f..c9b5a9195 100644 --- a/lib/sqlalchemy/ext/declarative/api.py +++ b/lib/sqlalchemy/ext/declarative/api.py @@ -18,7 +18,7 @@ import weakref from .base import _as_declarative, \ _declarative_constructor,\ - _MapperConfig, _add_attribute + _DeferredMapperConfig, _add_attribute from .clsregistry import _class_resolver @@ -468,8 +468,7 @@ class DeferredReflection(object): """Reflect all :class:`.Table` objects for all current :class:`.DeferredReflection` subclasses""" - to_map = [m for m in _MapperConfig.configs.values() - if issubclass(m.cls, cls)] + to_map = _DeferredMapperConfig.classes_for_base(cls) for thingy in to_map: cls._sa_decl_prepare(thingy.local_table, engine) thingy.map() @@ -479,7 +478,7 @@ class DeferredReflection(object): if isinstance(rel, properties.RelationshipProperty) and \ rel.secondary is not None: if isinstance(rel.secondary, Table): - cls._sa_decl_prepare(rel.secondary, engine) + cls._reflect_table(rel.secondary, engine) elif isinstance(rel.secondary, _class_resolver): rel.secondary._resolvers += ( cls._sa_deferred_table_resolver(engine, metadata), @@ -489,7 +488,7 @@ class DeferredReflection(object): def _sa_deferred_table_resolver(cls, engine, metadata): def _resolve(key): t1 = Table(key, metadata) - cls._sa_decl_prepare(t1, engine) + cls._reflect_table(t1, engine) return t1 return _resolve @@ -500,10 +499,14 @@ class DeferredReflection(object): # will fill in db-loaded columns # into the existing Table object. if local_table is not None: - Table(local_table.name, - local_table.metadata, - extend_existing=True, - autoload_replace=False, - autoload=True, - autoload_with=engine, - schema=local_table.schema) + cls._reflect_table(local_table, engine) + + @classmethod + def _reflect_table(cls, table, engine): + Table(table.name, + table.metadata, + extend_existing=True, + autoload_replace=False, + autoload=True, + autoload_with=engine, + schema=table.schema) -- cgit v1.2.1 From f89d4d216bd7605c920b7b8a10ecde6bfea2238c Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Sun, 5 Jan 2014 16:57:05 -0500 Subject: - happy new year --- lib/sqlalchemy/ext/declarative/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/sqlalchemy/ext/declarative/api.py') diff --git a/lib/sqlalchemy/ext/declarative/api.py b/lib/sqlalchemy/ext/declarative/api.py index c9b5a9195..2418c6e50 100644 --- a/lib/sqlalchemy/ext/declarative/api.py +++ b/lib/sqlalchemy/ext/declarative/api.py @@ -1,5 +1,5 @@ # ext/declarative/api.py -# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors +# Copyright (C) 2005-2014 the SQLAlchemy authors and contributors # # This module is part of SQLAlchemy and is released under # the MIT License: http://www.opensource.org/licenses/mit-license.php -- cgit v1.2.1