column_property(), relationship(), composite().
All MapperProperty classes have an auto-creating .info
dict available overall.
Both features should be avoided, however.
[ticket:2372]
+ - [feature] Added support for .info dictionary argument to
+ column_property(), relationship(), composite().
+ All MapperProperty classes have an auto-creating .info
+ dict available overall.
+
- [feature] Adding/removing None from a mapped collection
now generates attribute events. Previously, a None
append would be ignored in some cases. Related
constructs, the ability to alter those constructs, usually via the ALTER statement
as well as other database-specific constructs, is outside of the scope of SQLAlchemy
itself. While it's easy enough to emit ALTER statements and similar by hand,
-such as by passing a string to :meth:`.Connection.execute` or by using the
-:class:`.DDL` construct, it's a common practice to automate the maintenance of
+such as by passing a string to :meth:`.Connection.execute` or by using the
+:class:`.DDL` construct, it's a common practice to automate the maintenance of
database schemas in relation to application code using schema migration tools.
There are two major migration tools available for SQLAlchemy:
* `Alembic <http://alembic.readthedocs.org>`_ - Written by the author of SQLAlchemy,
Alembic features a highly customizable environment and a minimalistic usage pattern,
supporting such features as transactional DDL, automatic generation of "candidate"
- migrations, an "offline" mode which generates SQL scripts, and support for branch
+ migrations, an "offline" mode which generates SQL scripts, and support for branch
resolution.
* `SQLAlchemy-Migrate <http://code.google.com/p/sqlalchemy-migrate/>`_ - The original
migration tool for SQLAlchemy, SQLAlchemy-Migrate is widely used and continues
- under active development. SQLAlchemy-Migrate includes features such as
- SQL script generation, ORM class generation, ORM model comparison, and extensive
+ under active development. SQLAlchemy-Migrate includes features such as
+ SQL script generation, ORM class generation, ORM model comparison, and extensive
support for SQLite migrations.
.. _metadata_binding:
.. autoclass:: SchemaItem
:show-inheritance:
+ :members:
.. autoclass:: Table
:members:
The :class:`.Table` is the SQLAlchemy Core construct that allows one to define
table metadata, which among other things can be used by the SQLAlchemy ORM
as a target to map a class. The :ref:`Declarative <declarative_toplevel>`
-extension allows the :class:`.Table` object to be created automatically, given
+extension allows the :class:`.Table` object to be created automatically, given
the contents of the table primarily as a mapping of :class:`.Column` objects.
To apply table-level constraint objects such as :class:`.ForeignKeyConstraint`
-to a table defined using Declarative, use the ``__table_args__`` attribute,
+to a table defined using Declarative, use the ``__table_args__`` attribute,
described at :ref:`declarative_table_args`.
Constraints API
CREATE INDEX idx_col34 ON mytable (col3, col4){stop}
Note in the example above, the :class:`.Index` construct is created
-externally to the table which it corresponds, using :class:`.Column`
+externally to the table which it corresponds, using :class:`.Column`
objects directly. :class:`.Index` also supports
-"inline" definition inside the :class:`.Table`, using string names to
+"inline" definition inside the :class:`.Table`, using string names to
identify columns::
meta = MetaData()
event.listen(
users,
- "after_create",
+ "after_create",
AddConstraint(constraint)
)
event.listen(
DROP TABLE users{stop}
The real usefulness of the above becomes clearer once we illustrate the :meth:`.DDLEvent.execute_if`
-method. This method returns a modified form of the DDL callable which will
+method. This method returns a modified form of the DDL callable which will
filter on criteria before responding to a received event. It accepts a
parameter ``dialect``, which is the string name of a dialect or a tuple of such,
which will limit the execution of the item to just those dialects. It also
-accepts a ``callable_`` parameter which may reference a Python callable which will
+accepts a ``callable_`` parameter which may reference a Python callable which will
be invoked upon event reception, returning ``True`` or ``False`` indicating if
the event should proceed.
@property
def info(self):
- """A collection of per-DB-API connection instance properties."""
+ """Info dictionary associated with the underlying DBAPI connection
+ referred to by this :class:`.Connection`, allowing user-defined
+ data to be associated with the connection.
+
+ The data here will follow along with the DBAPI connection including
+ after it is returned to the connection pool and used again
+ in subsequent instances of :class:`.Connection`.
+
+ """
return self.connection.info
more specific system of describing which columns in a particular
``primaryjoin`` should be considered "foreign".
+ :param info: Optional data dictionary which will be populated into the
+ :attr:`.MapperProperty.info` attribute of this object.
+
+ .. versionadded:: 0.8
+
:param innerjoin=False:
when ``True``, joined eager loads will use an inner join to join
against related tables instead of an outer join. The purpose
.. versionadded:: 0.7.3
+ :param info: Optional data dictionary which will be populated into the
+ :attr:`.MapperProperty.info` attribute of this object.
+
+ .. versionadded:: 0.8
+
:param extension:
an
:class:`.AttributeExtension`
optional string that will be applied as the doc on the
class-bound descriptor.
+ :param info: Optional data dictionary which will be populated into the
+ :attr:`.MapperProperty.info` attribute of this object.
+
+ .. versionadded:: 0.8
+
:param extension:
an :class:`.AttributeExtension` instance,
or list of extensions, which will be prepended to the list of
self.group = kwargs.get('group', None)
self.comparator_factory = kwargs.pop('comparator_factory',
self.__class__.Comparator)
+ if 'info' in kwargs:
+ self.info = kwargs.pop('info')
+
util.set_creation_order(self)
self._create_descriptor()
def instrument_class(self, mapper): # pragma: no-coverage
raise NotImplementedError()
+ @util.memoized_property
+ def info(self):
+ """Info dictionary associated with the object, allowing user-defined
+ data to be associated with this :class:`.MapperProperty`.
+
+ The dictionary is generated when first accessed. Alternatively,
+ it can be specified as a constructor argument to the
+ :func:`.column_property`, :func:`.relationship`, or :func:`.composite`
+ functions.
+
+ .. versionadded:: 0.8 Added support for .info to all
+ :class:`.MapperProperty` subclasses.
+
+ """
+ return {}
+
_configure_started = False
_configure_finished = False
:param extension:
+ :param info: Optional data dictionary which will be populated into the
+ :attr:`.info` attribute of this object.
+
"""
self._orig_columns = [expression._labeled(c) for c in columns]
self.columns = [expression._labeled(_orm_full_deannotate(c))
self.active_history = kwargs.pop('active_history', False)
self.expire_on_flush = kwargs.pop('expire_on_flush', True)
+ if 'info' in kwargs:
+ self.info = kwargs.pop('info')
+
if 'doc' in kwargs:
self.doc = kwargs.pop('doc')
else:
cascade_backrefs=True,
load_on_pending=False,
strategy_class=None, _local_remote_pairs=None,
- query_class=None):
+ query_class=None,
+ info=None):
self.uselist = uselist
self.argument = argument
self.comparator = self.comparator_factory(self, None)
util.set_creation_order(self)
+ if info is not None:
+ self.info = info
+
if strategy_class:
self.strategy_class = strategy_class
elif self.lazy == 'dynamic':
def __init__(self, pool):
self.__pool = pool
self.connection = self.__connect()
- self.info = {}
pool.dispatch.first_connect.\
for_modify(pool.dispatch).\
exec_once(self.connection, self)
pool.dispatch.connect(self.connection, self)
+ @util.memoized_property
+ def info(self):
+ return {}
+
def close(self):
if self.connection is not None:
self.__pool.logger.debug("Closing connection %r", self.connection)
"""Proxies a DB-API connection and provides return-on-dereference
support."""
- __slots__ = '_pool', '__counter', 'connection', \
- '_connection_record', '__weakref__', \
- '_detached_info', '_echo'
-
def __init__(self, pool):
self._pool = pool
self.__counter = 0
conn = self.connection = self._connection_record.get_connection()
rec.fairy = weakref.ref(
self,
- lambda ref:_finalize_fairy and _finalize_fairy(conn, rec, pool, ref, _echo)
+ lambda ref: _finalize_fairy and \
+ _finalize_fairy(conn, rec, pool, ref, _echo)
)
_refs.add(rec)
except:
def is_valid(self):
return self.connection is not None
- @property
+ @util.memoized_property
def info(self):
- """An info collection unique to this DB-API connection."""
+ """Info dictionary associated with the underlying DBAPI connection
+ referred to by this :class:`.ConnectionFairy`, allowing user-defined
+ data to be associated with the connection.
+
+ The data here will follow along with the DBAPI connection including
+ after it is returned to the connection pool and used again
+ in subsequent instances of :class:`.ConnectionFairy`.
+ """
try:
return self._connection_record.info
except AttributeError:
- if self.connection is None:
- raise exc.InvalidRequestError("This connection is closed")
- try:
- return self._detached_info
- except AttributeError:
- self._detached_info = value = {}
- return value
+ raise exc.InvalidRequestError("This connection is closed")
def invalidate(self, e=None):
"""Mark this connection as invalidated.
self._connection_record.fairy = None
self._connection_record.connection = None
self._pool._do_return_conn(self._connection_record)
- self._detached_info = \
- self._connection_record.info.copy()
+ self.info = self.info.copy()
self._connection_record = None
def close(self):
@util.memoized_property
def info(self):
+ """Info dictionary associated with the object, allowing user-defined
+ data to be associated with this :class:`.SchemaItem`.
+
+ The dictionary is automatically generated when first accessed.
+ It can also be specified in the constructor of some objects,
+ such as :class:`.Table` and :class:`.Column`.
+
+ """
return {}
def _get_table_key(name, schema):
``Table`` object. Defaults to ``None`` which indicates all columns
should be reflected.
- :param info: A dictionary which defaults to ``{}``. A space to store
- application specific data. This must be a dictionary.
+ :param info: Optional data dictionary which will be populated into the
+ :attr:`.SchemaItem.info` attribute of this object.
:param keep_existing: When ``True``, indicates that if this Table
is already present in the given :class:`.MetaData`, ignore
self, include_columns, exclude_columns
)
- @util.memoized_property
- def info(self):
- """Dictionary provided for storage of additional information."""
- return {}
-
@property
def _sorted_constraints(self):
"""Return the set of constraints as a list, sorted by creation order."""
contain multiple columns, use the :class:`.Index` construct
instead.
- :param info: A dictionary which defaults to ``{}``. A space to store
- application specific data. This must be a dictionary.
+ :param info: Optional data dictionary which will be populated into the
+ :attr:`.SchemaItem.info` attribute of this object.
:param nullable: If set to the default of ``True``, indicates the
column will be rendered as allowing NULL, else it's rendered as
else:
return self.description
- @util.memoized_property
- def info(self):
- """Dictionary provided for storage of additional information."""
- return {}
-
def references(self, column):
"""Return True if this Column references the given column via foreign
key."""
lazy_gc()
self.assert_(p.checkedout() == 0)
- def test_properties(self):
+ def test_info(self):
p = self._queuepool_fixture(pool_size=1, max_overflow=0)
c = p.connect()
})
assert m.get_property('addresses')
+ def test_info(self):
+ users = self.tables.users
+ Address = self.classes.Address
+ class MyComposite(object):
+ pass
+ for constructor, args in [
+ (column_property, (users.c.name,)),
+ (relationship, (Address,)),
+ (composite, (MyComposite, 'id', 'name'))
+ ]:
+ obj = constructor(*args, info={"x": "y"})
+ eq_(obj.info, {"x": "y"})
+ obj.info["q"] = "p"
+ eq_(obj.info, {"x": "y", "q": "p"})
+
+ obj = constructor(*args)
+ eq_(obj.info, {})
+ obj.info["q"] = "p"
+ eq_(obj.info, {"q": "p"})
+
+
def test_add_property(self):
users, addresses, Address = (self.tables.users,
self.tables.addresses,