summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/ext
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2012-07-16 17:29:02 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2012-07-16 17:29:02 -0400
commitce9a702dbd52946487f45b98ef20d1b7783facb6 (patch)
tree7273a1982850bf9975509295d766053d4fe822b1 /lib/sqlalchemy/ext
parent1dc09bf6ede97ef08b2c8c0886a03b44bba735ff (diff)
downloadsqlalchemy-ce9a702dbd52946487f45b98ef20d1b7783facb6.tar.gz
- express most of the orm.util functions in terms of the inspection system
- modify inspection system: 1. raise a new exception for any case where the inspection context can't be returned. this supersedes the "not mapped" errors. 2. don't configure mappers on a mapper inspection. this allows the inspectors to be used during mapper config time. instead, the mapper configures on "with_polymorphic_selectable" now, which is needed for all queries - add a bunch of new "is_XYZ" attributes to inspectors - finish making the name change of "compile" -> "configure", for some reason this was only done partially
Diffstat (limited to 'lib/sqlalchemy/ext')
-rwxr-xr-xlib/sqlalchemy/ext/declarative.py202
-rw-r--r--lib/sqlalchemy/ext/hybrid.py192
2 files changed, 197 insertions, 197 deletions
diff --git a/lib/sqlalchemy/ext/declarative.py b/lib/sqlalchemy/ext/declarative.py
index 651a94970..ea0367d72 100755
--- a/lib/sqlalchemy/ext/declarative.py
+++ b/lib/sqlalchemy/ext/declarative.py
@@ -51,7 +51,7 @@ automatically named with the name of the attribute to which they are
assigned.
To name columns explicitly with a name distinct from their mapped attribute,
-just give the column a name. Below, column "some_table_id" is mapped to the
+just give the column a name. Below, column "some_table_id" is mapped to the
"id" attribute of `SomeClass`, but in SQL will be represented as "some_table_id"::
class SomeClass(Base):
@@ -68,7 +68,7 @@ added to the underlying :class:`.Table` and
Classes which are constructed using declarative can interact freely
with classes that are mapped explicitly with :func:`mapper`.
-It is recommended, though not required, that all tables
+It is recommended, though not required, that all tables
share the same underlying :class:`~sqlalchemy.schema.MetaData` object,
so that string-configured :class:`~sqlalchemy.schema.ForeignKey`
references can be resolved without issue.
@@ -98,9 +98,9 @@ of construction, the ``bind`` argument is accepted::
:func:`declarative_base` can also receive a pre-existing
:class:`.MetaData` object, which allows a
-declarative setup to be associated with an already
+declarative setup to be associated with an already
existing traditional collection of :class:`~sqlalchemy.schema.Table`
-objects::
+objects::
mymetadata = MetaData()
Base = declarative_base(metadata=mymetadata)
@@ -113,7 +113,7 @@ feature that the class specified to :func:`~sqlalchemy.orm.relationship`
may be a string name. The "class registry" associated with ``Base``
is used at mapper compilation time to resolve the name into the actual
class object, which is expected to have been defined once the mapper
-configuration is used::
+configuration is used::
class User(Base):
__tablename__ = 'users'
@@ -131,7 +131,7 @@ configuration is used::
Column constructs, since they are just that, are immediately usable,
as below where we define a primary join condition on the ``Address``
-class using them::
+class using them::
class Address(Base):
__tablename__ = 'addresses'
@@ -148,15 +148,15 @@ evaluated as Python expressions. The full namespace available within
this evaluation includes all classes mapped for this declarative base,
as well as the contents of the ``sqlalchemy`` package, including
expression functions like :func:`~sqlalchemy.sql.expression.desc` and
-:attr:`~sqlalchemy.sql.expression.func`::
+:attr:`~sqlalchemy.sql.expression.func`::
class User(Base):
# ....
addresses = relationship("Address",
- order_by="desc(Address.email)",
+ order_by="desc(Address.email)",
primaryjoin="Address.user_id==User.id")
-As an alternative to string-based attributes, attributes may also be
+As an alternative to string-based attributes, attributes may also be
defined after all classes have been created. Just add them to the target
class after the fact::
@@ -169,8 +169,8 @@ Configuring Many-to-Many Relationships
Many-to-many relationships are also declared in the same way
with declarative as with traditional mappings. The
``secondary`` argument to
-:func:`.relationship` is as usual passed a
-:class:`.Table` object, which is typically declared in the
+:func:`.relationship` is as usual passed a
+:class:`.Table` object, which is typically declared in the
traditional way. The :class:`.Table` usually shares
the :class:`.MetaData` object used by the declarative base::
@@ -185,7 +185,7 @@ the :class:`.MetaData` object used by the declarative base::
id = Column(Integer, primary_key=True)
keywords = relationship("Keyword", secondary=keywords)
-Like other :func:`.relationship` arguments, a string is accepted as well,
+Like other :func:`.relationship` arguments, a string is accepted as well,
passing the string name of the table as defined in the ``Base.metadata.tables``
collection::
@@ -194,7 +194,7 @@ collection::
id = Column(Integer, primary_key=True)
keywords = relationship("Keyword", secondary="keywords")
-As with traditional mapping, its generally not a good idea to use
+As with traditional mapping, its generally not a good idea to use
a :class:`.Table` as the "secondary" argument which is also mapped to
a class, unless the :class:`.relationship` is declared with ``viewonly=True``.
Otherwise, the unit-of-work system may attempt duplicate INSERT and
@@ -219,7 +219,7 @@ This attribute accommodates both positional as well as keyword
arguments that are normally sent to the
:class:`~sqlalchemy.schema.Table` constructor.
The attribute can be specified in one of two forms. One is as a
-dictionary::
+dictionary::
class MyClass(Base):
__tablename__ = 'sometable'
@@ -235,7 +235,7 @@ The other, a tuple, where each argument is positional
UniqueConstraint('foo'),
)
-Keyword arguments can be specified with the above form by
+Keyword arguments can be specified with the above form by
specifying the last argument as a dictionary::
class MyClass(Base):
@@ -253,7 +253,7 @@ As an alternative to ``__tablename__``, a direct
:class:`~sqlalchemy.schema.Table` construct may be used. The
:class:`~sqlalchemy.schema.Column` objects, which in this case require
their names, will be added to the mapping just like a regular mapping
-to a table::
+to a table::
class MyClass(Base):
__table__ = Table('my_table', Base.metadata,
@@ -277,9 +277,9 @@ and pass it to declarative classes::
class Address(Base):
__table__ = metadata.tables['address']
-Some configuration schemes may find it more appropriate to use ``__table__``,
-such as those which already take advantage of the data-driven nature of
-:class:`.Table` to customize and/or automate schema definition.
+Some configuration schemes may find it more appropriate to use ``__table__``,
+such as those which already take advantage of the data-driven nature of
+:class:`.Table` to customize and/or automate schema definition.
Note that when the ``__table__`` approach is used, the object is immediately
usable as a plain :class:`.Table` within the class declaration body itself,
@@ -292,10 +292,10 @@ by using the ``id`` column in the ``primaryjoin`` condition of a :func:`.relatio
Column('name', String(50))
)
- widgets = relationship(Widget,
+ widgets = relationship(Widget,
primaryjoin=Widget.myclass_id==__table__.c.id)
-Similarly, mapped attributes which refer to ``__table__`` can be placed inline,
+Similarly, mapped attributes which refer to ``__table__`` can be placed inline,
as below where we assign the ``name`` column to the attribute ``_name``, generating
a synonym for ``name``::
@@ -320,13 +320,13 @@ It's easy to set up a :class:`.Table` that uses ``autoload=True``
in conjunction with a mapped class::
class MyClass(Base):
- __table__ = Table('mytable', Base.metadata,
+ __table__ = Table('mytable', Base.metadata,
autoload=True, autoload_with=some_engine)
-However, one improvement that can be made here is to not
-require the :class:`.Engine` to be available when classes are
+However, one improvement that can be made here is to not
+require the :class:`.Engine` to be available when classes are
being first declared. To achieve this, use the
-:class:`.DeferredReflection` mixin, which sets up mappings
+:class:`.DeferredReflection` mixin, which sets up mappings
only after a special ``prepare(engine)`` step is called::
from sqlalchemy.ext.declarative import declarative_base, DeferredReflection
@@ -340,7 +340,7 @@ only after a special ``prepare(engine)`` step is called::
class Bar(Base):
__tablename__ = 'bar'
- # illustrate overriding of "bar.foo_id" to have
+ # illustrate overriding of "bar.foo_id" to have
# a foreign key constraint otherwise not
# reflected, such as when using MySQL
foo_id = Column(Integer, ForeignKey('foo.id'))
@@ -357,7 +357,7 @@ Declarative makes use of the :func:`~.orm.mapper` function internally
when it creates the mapping to the declared table. The options
for :func:`~.orm.mapper` are passed directly through via the ``__mapper_args__``
class attribute. As always, arguments which reference locally
-mapped columns can reference them directly from within the
+mapped columns can reference them directly from within the
class declaration::
from datetime import datetime
@@ -386,7 +386,7 @@ as declarative will determine this from the class itself. The various
Joined Table Inheritance
~~~~~~~~~~~~~~~~~~~~~~~~
-Joined table inheritance is defined as a subclass that defines its own
+Joined table inheritance is defined as a subclass that defines its own
table::
class Person(Base):
@@ -419,13 +419,13 @@ only the ``engineers.id`` column, give it a different attribute name::
.. versionchanged:: 0.7 joined table inheritance favors the subclass
column over that of the superclass, such as querying above
for ``Engineer.id``. Prior to 0.7 this was the reverse.
-
+
Single Table Inheritance
~~~~~~~~~~~~~~~~~~~~~~~~
Single table inheritance is defined as a subclass that does not have
its own table; you just leave out the ``__table__`` and ``__tablename__``
-attributes::
+attributes::
class Person(Base):
__tablename__ = 'people'
@@ -536,7 +536,7 @@ To have a concrete ``employee`` table, use :class:`.ConcreteBase` instead::
employee_id = Column(Integer, primary_key=True)
name = Column(String(50))
__mapper_args__ = {
- 'polymorphic_identity':'employee',
+ 'polymorphic_identity':'employee',
'concrete':True}
@@ -548,7 +548,7 @@ Either ``Employee`` base can be used in the normal fashion::
name = Column(String(50))
manager_data = Column(String(40))
__mapper_args__ = {
- 'polymorphic_identity':'manager',
+ 'polymorphic_identity':'manager',
'concrete':True}
class Engineer(Employee):
@@ -556,7 +556,7 @@ Either ``Employee`` base can be used in the normal fashion::
employee_id = Column(Integer, primary_key=True)
name = Column(String(50))
engineer_info = Column(String(40))
- __mapper_args__ = {'polymorphic_identity':'engineer',
+ __mapper_args__ = {'polymorphic_identity':'engineer',
'concrete':True}
@@ -596,29 +596,29 @@ idioms is below::
Where above, the class ``MyModel`` will contain an "id" column
as the primary key, a ``__tablename__`` attribute that derives
-from the name of the class itself, as well as ``__table_args__``
+from the name of the class itself, as well as ``__table_args__``
and ``__mapper_args__`` defined by the ``MyMixin`` mixin class.
-There's no fixed convention over whether ``MyMixin`` precedes
-``Base`` or not. Normal Python method resolution rules apply, and
+There's no fixed convention over whether ``MyMixin`` precedes
+``Base`` or not. Normal Python method resolution rules apply, and
the above example would work just as well with::
class MyModel(Base, MyMixin):
name = Column(String(1000))
-This works because ``Base`` here doesn't define any of the
-variables that ``MyMixin`` defines, i.e. ``__tablename__``,
-``__table_args__``, ``id``, etc. If the ``Base`` did define
-an attribute of the same name, the class placed first in the
-inherits list would determine which attribute is used on the
+This works because ``Base`` here doesn't define any of the
+variables that ``MyMixin`` defines, i.e. ``__tablename__``,
+``__table_args__``, ``id``, etc. If the ``Base`` did define
+an attribute of the same name, the class placed first in the
+inherits list would determine which attribute is used on the
newly defined class.
Augmenting the Base
~~~~~~~~~~~~~~~~~~~
-In addition to using a pure mixin, most of the techniques in this
+In addition to using a pure mixin, most of the techniques in this
section can also be applied to the base class itself, for patterns that
-should apply to all classes derived from a particular base. This
+should apply to all classes derived from a particular base. This
is achieved using the ``cls`` argument of the :func:`.declarative_base` function::
from sqlalchemy.ext.declarative import declared_attr
@@ -639,14 +639,14 @@ is achieved using the ``cls`` argument of the :func:`.declarative_base` function
class MyModel(Base):
name = Column(String(1000))
-Where above, ``MyModel`` and all other classes that derive from ``Base`` will have
-a table name derived from the class name, an ``id`` primary key column, as well as
+Where above, ``MyModel`` and all other classes that derive from ``Base`` will have
+a table name derived from the class name, an ``id`` primary key column, as well as
the "InnoDB" engine for MySQL.
Mixing in Columns
~~~~~~~~~~~~~~~~~
-The most basic way to specify a column on a mixin is by simple
+The most basic way to specify a column on a mixin is by simple
declaration::
class TimestampMixin(object):
@@ -659,26 +659,26 @@ declaration::
name = Column(String(1000))
Where above, all declarative classes that include ``TimestampMixin``
-will also have a column ``created_at`` that applies a timestamp to
+will also have a column ``created_at`` that applies a timestamp to
all row insertions.
-Those familiar with the SQLAlchemy expression language know that
+Those familiar with the SQLAlchemy expression language know that
the object identity of clause elements defines their role in a schema.
-Two ``Table`` objects ``a`` and ``b`` may both have a column called
-``id``, but the way these are differentiated is that ``a.c.id``
+Two ``Table`` objects ``a`` and ``b`` may both have a column called
+``id``, but the way these are differentiated is that ``a.c.id``
and ``b.c.id`` are two distinct Python objects, referencing their
parent tables ``a`` and ``b`` respectively.
In the case of the mixin column, it seems that only one
-:class:`.Column` object is explicitly created, yet the ultimate
+:class:`.Column` object is explicitly created, yet the ultimate
``created_at`` column above must exist as a distinct Python object
for each separate destination class. To accomplish this, the declarative
-extension creates a **copy** of each :class:`.Column` object encountered on
+extension creates a **copy** of each :class:`.Column` object encountered on
a class that is detected as a mixin.
This copy mechanism is limited to simple columns that have no foreign
keys, as a :class:`.ForeignKey` itself contains references to columns
-which can't be properly recreated at this level. For columns that
+which can't be properly recreated at this level. For columns that
have foreign keys, as well as for the variety of mapper-level constructs
that require destination-explicit context, the
:func:`~.declared_attr` decorator is provided so that
@@ -695,7 +695,7 @@ patterns common to many classes can be defined as callables::
__tablename__ = 'user'
id = Column(Integer, primary_key=True)
-Where above, the ``address_id`` class-level callable is executed at the
+Where above, the ``address_id`` class-level callable is executed at the
point at which the ``User`` class is constructed, and the declarative
extension can use the resulting :class:`.Column` object as returned by
the method without the need to copy it.
@@ -704,8 +704,8 @@ the method without the need to copy it.
Rename 0.6.5 ``sqlalchemy.util.classproperty`` into :func:`~.declared_attr`.
Columns generated by :func:`~.declared_attr` can also be
-referenced by ``__mapper_args__`` to a limited degree, currently
-by ``polymorphic_on`` and ``version_id_col``, by specifying the
+referenced by ``__mapper_args__`` to a limited degree, currently
+by ``polymorphic_on`` and ``version_id_col``, by specifying the
classdecorator itself into the dictionary - the declarative extension
will resolve them at class construction time::
@@ -753,7 +753,7 @@ reference a common target class via many-to-one::
id = Column(Integer, primary_key=True)
:func:`~sqlalchemy.orm.relationship` definitions which require explicit
-primaryjoin, order_by etc. expressions should use the string forms
+primaryjoin, order_by etc. expressions should use the string forms
for these arguments, so that they are evaluated as late as possible.
To reference the mixin class in these expressions, use the given ``cls``
to get it's name::
@@ -775,8 +775,8 @@ Mixing in deferred(), column_property(), etc.
Like :func:`~sqlalchemy.orm.relationship`, all
:class:`~sqlalchemy.orm.interfaces.MapperProperty` subclasses such as
:func:`~sqlalchemy.orm.deferred`, :func:`~sqlalchemy.orm.column_property`,
-etc. ultimately involve references to columns, and therefore, when
-used with declarative mixins, have the :func:`.declared_attr`
+etc. ultimately involve references to columns, and therefore, when
+used with declarative mixins, have the :func:`.declared_attr`
requirement so that no reliance on copying is needed::
class SomethingMixin(object):
@@ -793,7 +793,7 @@ Controlling table inheritance with mixins
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The ``__tablename__`` attribute in conjunction with the hierarchy of
-classes involved in a declarative mixin scenario controls what type of
+classes involved in a declarative mixin scenario controls what type of
table inheritance, if any,
is configured by the declarative extension.
@@ -828,7 +828,7 @@ return a ``__tablename__`` in the event that no table is already
mapped in the inheritance hierarchy. To help with this, a
:func:`~sqlalchemy.ext.declarative.has_inherited_table` helper
function is provided that returns ``True`` if a parent class already
-has a mapped table.
+has a mapped table.
As an example, here's a mixin that will only allow single table
inheritance::
@@ -918,7 +918,7 @@ from multiple collections::
Creating Indexes with Mixins
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-To define a named, potentially multicolumn :class:`.Index` that applies to all
+To define a named, potentially multicolumn :class:`.Index` that applies to all
tables derived from a mixin, use the "inline" form of :class:`.Index` and establish
it as part of ``__table_args__``::
@@ -940,7 +940,7 @@ Special Directives
``__declare_last__()``
~~~~~~~~~~~~~~~~~~~~~~
-The ``__declare_last__()`` hook allows definition of
+The ``__declare_last__()`` hook allows definition of
a class level function that is automatically called by the :meth:`.MapperEvents.after_configured`
event, which occurs after mappings are assumed to be completed and the 'configure' step
has finished::
@@ -989,7 +989,7 @@ bases::
__abstract__ = True
metadata = MetaData()
-Above, classes which inherit from ``DefaultBase`` will use one :class:`.MetaData` as the
+Above, classes which inherit from ``DefaultBase`` will use one :class:`.MetaData` as the
registry of tables, and those which inherit from ``OtherBase`` will use a different one.
The tables themselves can then be created perhaps within distinct databases::
@@ -1022,7 +1022,7 @@ setup using :func:`~sqlalchemy.orm.scoped_session` might look like::
Base = declarative_base()
Mapped instances then make usage of
-:class:`~sqlalchemy.orm.session.Session` in the usual way.
+:class:`~sqlalchemy.orm.session.Session` in the usual way.
"""
@@ -1047,14 +1047,14 @@ def _declared_mapping_info(cls):
return _MapperConfig.configs[cls]
# regular mapping
elif _is_mapped_class(cls):
- return class_mapper(cls, compile=False)
+ return class_mapper(cls, configure=False)
else:
return None
def instrument_declarative(cls, registry, metadata):
"""Given a class, configure the class declaratively,
using the given registry, which can be any dictionary, and
- MetaData object.
+ MetaData object.
"""
if '_decl_class_registry' in cls.__dict__:
@@ -1097,7 +1097,7 @@ def _as_declarative(cls, classname, dict_):
def go():
cls.__declare_last__()
if '__abstract__' in base.__dict__:
- if (base is cls or
+ if (base is cls or
(base in cls.__bases__ and not _is_declarative_inherits)
):
return
@@ -1109,7 +1109,7 @@ def _as_declarative(cls, classname, dict_):
for name,obj in vars(base).items():
if name == '__mapper_args__':
if not mapper_args_fn and (
- not class_mapped or
+ not class_mapped or
isinstance(obj, declarative_props)
):
# don't even invoke __mapper_args__ until
@@ -1118,13 +1118,13 @@ def _as_declarative(cls, classname, dict_):
mapper_args_fn = lambda: cls.__mapper_args__
elif name == '__tablename__':
if not tablename and (
- not class_mapped or
+ not class_mapped or
isinstance(obj, declarative_props)
):
tablename = cls.__tablename__
elif name == '__table_args__':
if not table_args and (
- not class_mapped or
+ not class_mapped or
isinstance(obj, declarative_props)
):
table_args = cls.__table_args__
@@ -1139,7 +1139,7 @@ def _as_declarative(cls, classname, dict_):
util.warn("Regular (i.e. not __special__) "
"attribute '%s.%s' uses @declared_attr, "
"but owning class %s is mapped - "
- "not applying to subclass %s."
+ "not applying to subclass %s."
% (base.__name__, name, base, cls))
continue
elif base is not cls:
@@ -1151,7 +1151,7 @@ def _as_declarative(cls, classname, dict_):
"must be declared as @declared_attr callables "
"on declarative mixin classes. ")
if name not in dict_ and not (
- '__table__' in dict_ and
+ '__table__' in dict_ and
(obj.name or name) in dict_['__table__'].c
) and name not in potential_columns:
potential_columns[name] = \
@@ -1231,7 +1231,7 @@ def _as_declarative(cls, classname, dict_):
elif isinstance(c, Column):
_undefer_column_name(key, c)
declared_columns.add(c)
- # if the column is the same name as the key,
+ # if the column is the same name as the key,
# remove it from the explicit properties dict.
# the normal rules for assigning column-based properties
# will take over, including precedence of columns
@@ -1317,17 +1317,17 @@ def _as_declarative(cls, classname, dict_):
if c.name in inherited_table.c:
raise exc.ArgumentError(
"Column '%s' on class %s conflicts with "
- "existing column '%s'" %
+ "existing column '%s'" %
(c, cls, inherited_table.c[c.name])
)
inherited_table.append_column(c)
- mt = _MapperConfig(mapper_cls,
+ mt = _MapperConfig(mapper_cls,
cls, table,
inherits,
- declared_columns,
+ declared_columns,
column_copies,
- our_stuff,
+ our_stuff,
mapper_args_fn)
if not hasattr(cls, '_sa_decl_prepare'):
mt.map()
@@ -1335,9 +1335,9 @@ def _as_declarative(cls, classname, dict_):
class _MapperConfig(object):
configs = util.OrderedDict()
- def __init__(self, mapper_cls,
- cls,
- table,
+ def __init__(self, mapper_cls,
+ cls,
+ table,
inherits,
declared_columns,
column_copies,
@@ -1361,7 +1361,7 @@ class _MapperConfig(object):
else:
mapper_args = {}
- # make sure that column copies are used rather
+ # make sure that column copies are used rather
# than the original columns from any mixins
for k in ('version_id_col', 'polymorphic_on',):
if k in mapper_args:
@@ -1376,7 +1376,7 @@ class _MapperConfig(object):
if self.inherits and not mapper_args.get('concrete', False):
# single or joined inheritance
- # exclude any cols on the inherited table which are
+ # exclude any cols on the inherited table which are
# not mapped on the parent class, to avoid
# mapping columns specific to sibling/nephew classes
inherited_mapper = _declared_mapping_info(self.inherits)
@@ -1389,7 +1389,7 @@ class _MapperConfig(object):
exclude_properties.difference_update(
[c.key for c in self.declared_columns])
- # look through columns in the current mapper that
+ # look through columns in the current mapper that
# are keyed to a propname different than the colname
# (if names were the same, we'd have popped it out above,
# in which case the mapper makes this combination).
@@ -1440,7 +1440,7 @@ class DeclarativeMeta(type):
cls.__mapper__.add_property(key, value)
elif isinstance(value, MapperProperty):
cls.__mapper__.add_property(
- key,
+ key,
_deferred_relationship(cls, value)
)
else:
@@ -1454,7 +1454,7 @@ class _GetColumns(object):
self.cls = cls
def __getattr__(self, key):
- mapper = class_mapper(self.cls, compile=False)
+ mapper = class_mapper(self.cls, configure=False)
if mapper:
if not mapper.has_property(key):
raise exc.InvalidRequestError(
@@ -1511,7 +1511,7 @@ def _deferred_relationship(cls, prop):
"When initializing mapper %s, expression %r failed to "
"locate a name (%r). If this is a class name, consider "
"adding this relationship() to the %r class after "
- "both dependent classes have been defined." %
+ "both dependent classes have been defined." %
(prop.parent, arg, n.args[0], cls)
)
return return_cls
@@ -1582,13 +1582,13 @@ class declared_attr(property):
a mapped property or special declarative member name.
.. versionchanged:: 0.6.{2,3,4}
- ``@declared_attr`` is available as
+ ``@declared_attr`` is available as
``sqlalchemy.util.classproperty`` for SQLAlchemy versions
0.6.2, 0.6.3, 0.6.4.
@declared_attr turns the attribute into a scalar-like
property that can be invoked from the uninstantiated class.
- Declarative treats attributes specifically marked with
+ Declarative treats attributes specifically marked with
@declared_attr as returning a construct that is specific
to mapping or declarative table configuration. The name
of the attribute is that of what the non-dynamic version
@@ -1620,7 +1620,7 @@ class declared_attr(property):
def __mapper_args__(cls):
if cls.__name__ == 'Employee':
return {
- "polymorphic_on":cls.type,
+ "polymorphic_on":cls.type,
"polymorphic_identity":"Employee"
}
else:
@@ -1668,8 +1668,8 @@ def declarative_base(bind=None, metadata=None, mapper=None, cls=object,
:param bind: An optional
:class:`~sqlalchemy.engine.base.Connectable`, will be assigned
- the ``bind`` attribute on the :class:`~sqlalchemy.MetaData`
- instance.
+ the ``bind`` attribute on the :class:`~sqlalchemy.MetaData`
+ instance.
:param metadata:
An optional :class:`~sqlalchemy.MetaData` instance. All
@@ -1700,11 +1700,11 @@ def declarative_base(bind=None, metadata=None, mapper=None, cls=object,
no __init__ will be provided and construction will fall back to
cls.__init__ by way of the normal Python semantics.
- :param class_registry: optional dictionary that will serve as the
+ :param class_registry: optional dictionary that will serve as the
registry of class names-> mapped classes when string names
- are used to identify classes inside of :func:`.relationship`
+ are used to identify classes inside of :func:`.relationship`
and others. Allows two or more declarative base classes
- to share the same registry of class names for simplified
+ to share the same registry of class names for simplified
inter-base relationships.
:param metaclass:
@@ -1759,7 +1759,7 @@ class ConcreteBase(object):
employee_id = Column(Integer, primary_key=True)
name = Column(String(50))
__mapper_args__ = {
- 'polymorphic_identity':'employee',
+ 'polymorphic_identity':'employee',
'concrete':True}
class Manager(Employee):
@@ -1768,7 +1768,7 @@ class ConcreteBase(object):
name = Column(String(50))
manager_data = Column(String(40))
__mapper_args__ = {
- 'polymorphic_identity':'manager',
+ 'polymorphic_identity':'manager',
'concrete':True}
"""
@@ -1817,7 +1817,7 @@ class AbstractConcreteBase(ConcreteBase):
name = Column(String(50))
manager_data = Column(String(40))
__mapper_args__ = {
- 'polymorphic_identity':'manager',
+ 'polymorphic_identity':'manager',
'concrete':True}
"""
@@ -1851,7 +1851,7 @@ class AbstractConcreteBase(ConcreteBase):
class DeferredReflection(object):
- """A helper class for construction of mappings based on
+ """A helper class for construction of mappings based on
a deferred reflection step.
Normally, declarative can be used with reflection by
@@ -1882,9 +1882,9 @@ class DeferredReflection(object):
DeferredReflection.prepare(engine)
The :class:`.DeferredReflection` mixin can be applied to individual
- classes, used as the base for the declarative base itself,
+ classes, used as the base for the declarative base itself,
or used in a custom abstract class. Using an abstract base
- allows that only a subset of classes to be prepared for a
+ allows that only a subset of classes to be prepared for a
particular prepare step, which is necessary for applications
that use more than one engine. For example, if an application
has two engines, you might use two bases, and prepare each
diff --git a/lib/sqlalchemy/ext/hybrid.py b/lib/sqlalchemy/ext/hybrid.py
index 8734181ea..ebf061645 100644
--- a/lib/sqlalchemy/ext/hybrid.py
+++ b/lib/sqlalchemy/ext/hybrid.py
@@ -10,8 +10,8 @@
class level and at the instance level.
The :mod:`~sqlalchemy.ext.hybrid` extension provides a special form of method
-decorator, is around 50 lines of code and has almost no dependencies on the rest
-of SQLAlchemy. It can, in theory, work with any descriptor-based expression
+decorator, is around 50 lines of code and has almost no dependencies on the rest
+of SQLAlchemy. It can, in theory, work with any descriptor-based expression
system.
Consider a mapping ``Interval``, representing integer ``start`` and ``end``
@@ -25,9 +25,9 @@ as the class itself::
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import Session, aliased
from sqlalchemy.ext.hybrid import hybrid_property, hybrid_method
-
+
Base = declarative_base()
-
+
class Interval(Base):
__tablename__ = 'interval'
@@ -50,7 +50,7 @@ as the class itself::
@hybrid_method
def intersects(self, other):
return self.contains(other.start) | self.contains(other.end)
-
+
Above, the ``length`` property returns the difference between the ``end`` and
``start`` attributes. With an instance of ``Interval``, this subtraction occurs
in Python, using normal Python descriptor mechanics::
@@ -60,33 +60,33 @@ in Python, using normal Python descriptor mechanics::
5
When dealing with the ``Interval`` class itself, the :class:`.hybrid_property`
-descriptor evaluates the function body given the ``Interval`` class as
+descriptor evaluates the function body given the ``Interval`` class as
the argument, which when evaluated with SQLAlchemy expression mechanics
returns a new SQL expression::
-
+
>>> print Interval.length
interval."end" - interval.start
-
+
>>> print Session().query(Interval).filter(Interval.length > 10)
- SELECT interval.id AS interval_id, interval.start AS interval_start,
- interval."end" AS interval_end
- FROM interval
+ SELECT interval.id AS interval_id, interval.start AS interval_start,
+ interval."end" AS interval_end
+ FROM interval
WHERE interval."end" - interval.start > :param_1
-
-ORM methods such as :meth:`~.Query.filter_by` generally use ``getattr()`` to
+
+ORM methods such as :meth:`~.Query.filter_by` generally use ``getattr()`` to
locate attributes, so can also be used with hybrid attributes::
>>> print Session().query(Interval).filter_by(length=5)
- SELECT interval.id AS interval_id, interval.start AS interval_start,
- interval."end" AS interval_end
- FROM interval
+ SELECT interval.id AS interval_id, interval.start AS interval_start,
+ interval."end" AS interval_end
+ FROM interval
WHERE interval."end" - interval.start = :param_1
The ``Interval`` class example also illustrates two methods, ``contains()`` and ``intersects()``,
decorated with :class:`.hybrid_method`.
This decorator applies the same idea to methods that :class:`.hybrid_property` applies
-to attributes. The methods return boolean values, and take advantage
-of the Python ``|`` and ``&`` bitwise operators to produce equivalent instance-level and
+to attributes. The methods return boolean values, and take advantage
+of the Python ``|`` and ``&`` bitwise operators to produce equivalent instance-level and
SQL expression-level boolean behavior::
>>> i1.contains(6)
@@ -97,24 +97,24 @@ SQL expression-level boolean behavior::
True
>>> i1.intersects(Interval(25, 29))
False
-
+
>>> print Session().query(Interval).filter(Interval.contains(15))
- SELECT interval.id AS interval_id, interval.start AS interval_start,
- interval."end" AS interval_end
- FROM interval
+ SELECT interval.id AS interval_id, interval.start AS interval_start,
+ interval."end" AS interval_end
+ FROM interval
WHERE interval.start <= :start_1 AND interval."end" > :end_1
>>> ia = aliased(Interval)
>>> print Session().query(Interval, ia).filter(Interval.intersects(ia))
- SELECT interval.id AS interval_id, interval.start AS interval_start,
- interval."end" AS interval_end, interval_1.id AS interval_1_id,
- interval_1.start AS interval_1_start, interval_1."end" AS interval_1_end
- FROM interval, interval AS interval_1
- WHERE interval.start <= interval_1.start
- AND interval."end" > interval_1.start
- OR interval.start <= interval_1."end"
+ SELECT interval.id AS interval_id, interval.start AS interval_start,
+ interval."end" AS interval_end, interval_1.id AS interval_1_id,
+ interval_1.start AS interval_1_start, interval_1."end" AS interval_1_end
+ FROM interval, interval AS interval_1
+ WHERE interval.start <= interval_1.start
+ AND interval."end" > interval_1.start
+ OR interval.start <= interval_1."end"
AND interval."end" > interval_1."end"
-
+
Defining Expression Behavior Distinct from Attribute Behavior
--------------------------------------------------------------
@@ -122,18 +122,18 @@ Our usage of the ``&`` and ``|`` bitwise operators above was fortunate, consider
our functions operated on two boolean values to return a new one. In many cases, the construction
of an in-Python function and a SQLAlchemy SQL expression have enough differences that two
separate Python expressions should be defined. The :mod:`~sqlalchemy.ext.hybrid` decorators
-define the :meth:`.hybrid_property.expression` modifier for this purpose. As an example we'll
+define the :meth:`.hybrid_property.expression` modifier for this purpose. As an example we'll
define the radius of the interval, which requires the usage of the absolute value function::
from sqlalchemy import func
-
+
class Interval(object):
# ...
-
+
@hybrid_property
def radius(self):
return abs(self.length) / 2
-
+
@radius.expression
def radius(cls):
return func.abs(cls.length) / 2
@@ -143,22 +143,22 @@ Above the Python function ``abs()`` is used for instance-level operations, the S
>>> i1.radius
2
-
+
>>> print Session().query(Interval).filter(Interval.radius > 5)
- SELECT interval.id AS interval_id, interval.start AS interval_start,
- interval."end" AS interval_end
- FROM interval
+ SELECT interval.id AS interval_id, interval.start AS interval_start,
+ interval."end" AS interval_end
+ FROM interval
WHERE abs(interval."end" - interval.start) / :abs_1 > :param_1
Defining Setters
----------------
-Hybrid properties can also define setter methods. If we wanted ``length`` above, when
+Hybrid properties can also define setter methods. If we wanted ``length`` above, when
set, to modify the endpoint value::
class Interval(object):
# ...
-
+
@hybrid_property
def length(self):
return self.end - self.start
@@ -179,7 +179,7 @@ The ``length(self, value)`` method is now called upon set::
Working with Relationships
--------------------------
-There's no essential difference when creating hybrids that work with related objects as
+There's no essential difference when creating hybrids that work with related objects as
opposed to column-based data. The need for distinct expressions tends to be greater.
Consider the following declarative mapping which relates a ``User`` to a ``SavingsAccount``::
@@ -187,9 +187,9 @@ Consider the following declarative mapping which relates a ``User`` to a ``Savin
from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.hybrid import hybrid_property
-
+
Base = declarative_base()
-
+
class SavingsAccount(Base):
__tablename__ = 'account'
id = Column(Integer, primary_key=True)
@@ -200,9 +200,9 @@ Consider the following declarative mapping which relates a ``User`` to a ``Savin
__tablename__ = 'user'
id = Column(Integer, primary_key=True)
name = Column(String(100), nullable=False)
-
+
accounts = relationship("SavingsAccount", backref="owner")
-
+
@hybrid_property
def balance(self):
if self.accounts:
@@ -222,17 +222,17 @@ Consider the following declarative mapping which relates a ``User`` to a ``Savin
def balance(cls):
return SavingsAccount.balance
-The above hybrid property ``balance`` works with the first ``SavingsAccount`` entry in the list of
+The above hybrid property ``balance`` works with the first ``SavingsAccount`` entry in the list of
accounts for this user. The in-Python getter/setter methods can treat ``accounts`` as a Python
-list available on ``self``.
+list available on ``self``.
-However, at the expression level, we can't travel along relationships to column attributes
-directly since SQLAlchemy is explicit about joins. So here, it's expected that the ``User`` class will be
+However, at the expression level, we can't travel along relationships to column attributes
+directly since SQLAlchemy is explicit about joins. So here, it's expected that the ``User`` class will be
used in an appropriate context such that an appropriate join to ``SavingsAccount`` will be present::
>>> print Session().query(User, User.balance).join(User.accounts).filter(User.balance > 5000)
SELECT "user".id AS user_id, "user".name AS user_name, account.balance AS account_balance
- FROM "user" JOIN account ON "user".id = account.user_id
+ FROM "user" JOIN account ON "user".id = account.user_id
WHERE account.balance > :balance_1
Note however, that while the instance level accessors need to worry about whether ``self.accounts``
@@ -242,8 +242,8 @@ would use an outer join::
>>> from sqlalchemy import or_
>>> print (Session().query(User, User.balance).outerjoin(User.accounts).
... filter(or_(User.balance < 5000, User.balance == None)))
- SELECT "user".id AS user_id, "user".name AS user_name, account.balance AS account_balance
- FROM "user" LEFT OUTER JOIN account ON "user".id = account.user_id
+ SELECT "user".id AS user_id, "user".name AS user_name, account.balance AS account_balance
+ FROM "user" LEFT OUTER JOIN account ON "user".id = account.user_id
WHERE account.balance < :balance_1 OR account.balance IS NULL
.. _hybrid_custom_comparators:
@@ -253,7 +253,7 @@ Building Custom Comparators
The hybrid property also includes a helper that allows construction of custom comparators.
A comparator object allows one to customize the behavior of each SQLAlchemy expression
-operator individually. They are useful when creating custom types that have
+operator individually. They are useful when creating custom types that have
some highly idiosyncratic behavior on the SQL side.
The example class below allows case-insensitive comparisons on the attribute
@@ -263,9 +263,9 @@ named ``word_insensitive``::
from sqlalchemy import func, Column, Integer, String
from sqlalchemy.orm import Session
from sqlalchemy.ext.declarative import declarative_base
-
+
Base = declarative_base()
-
+
class CaseInsensitiveComparator(Comparator):
def __eq__(self, other):
return func.lower(self.__clause_element__()) == func.lower(other)
@@ -274,27 +274,27 @@ named ``word_insensitive``::
__tablename__ = 'searchword'
id = Column(Integer, primary_key=True)
word = Column(String(255), nullable=False)
-
+
@hybrid_property
def word_insensitive(self):
return self.word.lower()
-
+
@word_insensitive.comparator
def word_insensitive(cls):
return CaseInsensitiveComparator(cls.word)
-Above, SQL expressions against ``word_insensitive`` will apply the ``LOWER()``
+Above, SQL expressions against ``word_insensitive`` will apply the ``LOWER()``
SQL function to both sides::
>>> print Session().query(SearchWord).filter_by(word_insensitive="Trucks")
- SELECT searchword.id AS searchword_id, searchword.word AS searchword_word
- FROM searchword
+ SELECT searchword.id AS searchword_id, searchword.word AS searchword_word
+ FROM searchword
WHERE lower(searchword.word) = lower(:lower_1)
The ``CaseInsensitiveComparator`` above implements part of the :class:`.ColumnOperators`
interface. A "coercion" operation like lowercasing can be applied to all comparison operations
(i.e. ``eq``, ``lt``, ``gt``, etc.) using :meth:`.Operators.operate`::
-
+
class CaseInsensitiveComparator(Comparator):
def operate(self, op, other):
return op(func.lower(self.__clause_element__()), func.lower(other))
@@ -310,7 +310,7 @@ by ``@word_insensitive.comparator``, only applies to the SQL side.
A more comprehensive form of the custom comparator is to construct a *Hybrid Value Object*.
This technique applies the target value or expression to a value object which is then
returned by the accessor in all cases. The value object allows control
-of all operations upon the value as well as how compared values are treated, both
+of all operations upon the value as well as how compared values are treated, both
on the SQL expression side as well as the Python value side. Replacing the
previous ``CaseInsensitiveComparator`` class with a new ``CaseInsensitiveWord`` class::
@@ -342,8 +342,8 @@ previous ``CaseInsensitiveComparator`` class with a new ``CaseInsensitiveWord``
Above, the ``CaseInsensitiveWord`` object represents ``self.word``, which may be a SQL function,
or may be a Python native. By overriding ``operate()`` and ``__clause_element__()``
to work in terms of ``self.word``, all comparison operations will work against the
-"converted" form of ``word``, whether it be SQL side or Python side.
-Our ``SearchWord`` class can now deliver the ``CaseInsensitiveWord`` object unconditionally
+"converted" form of ``word``, whether it be SQL side or Python side.
+Our ``SearchWord`` class can now deliver the ``CaseInsensitiveWord`` object unconditionally
from a single hybrid call::
class SearchWord(Base):
@@ -356,12 +356,12 @@ from a single hybrid call::
return CaseInsensitiveWord(self.word)
The ``word_insensitive`` attribute now has case-insensitive comparison behavior
-universally, including SQL expression vs. Python expression (note the Python value is
+universally, including SQL expression vs. Python expression (note the Python value is
converted to lower case on the Python side here)::
>>> print Session().query(SearchWord).filter_by(word_insensitive="Trucks")
- SELECT searchword.id AS searchword_id, searchword.word AS searchword_word
- FROM searchword
+ SELECT searchword.id AS searchword_id, searchword.word AS searchword_word
+ FROM searchword
WHERE lower(searchword.word) = :lower_1
SQL expression versus SQL expression::
@@ -369,13 +369,13 @@ SQL expression versus SQL expression::
>>> sw1 = aliased(SearchWord)
>>> sw2 = aliased(SearchWord)
>>> print Session().query(
- ... sw1.word_insensitive,
+ ... sw1.word_insensitive,
... sw2.word_insensitive).\\
... filter(
... sw1.word_insensitive > sw2.word_insensitive
... )
- SELECT lower(searchword_1.word) AS lower_1, lower(searchword_2.word) AS lower_2
- FROM searchword AS searchword_1, searchword AS searchword_2
+ SELECT lower(searchword_1.word) AS lower_1, lower(searchword_2.word) AS lower_2
+ FROM searchword AS searchword_1, searchword AS searchword_2
WHERE lower(searchword_1.word) > lower(searchword_2.word)
Python only expression::
@@ -403,7 +403,7 @@ Building Transformers
----------------------
A *transformer* is an object which can receive a :class:`.Query` object and return a
-new one. The :class:`.Query` object includes a method :meth:`.with_transformation`
+new one. The :class:`.Query` object includes a method :meth:`.with_transformation`
that simply returns a new :class:`.Query` transformed by the given function.
We can combine this with the :class:`.Comparator` class to produce one type
@@ -412,18 +412,18 @@ filtering criterion.
Consider a mapped class ``Node``, which assembles using adjacency list into a hierarchical
tree pattern::
-
+
from sqlalchemy import Column, Integer, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
-
+
class Node(Base):
__tablename__ = 'node'
id =Column(Integer, primary_key=True)
parent_id = Column(Integer, ForeignKey('node.id'))
parent = relationship("Node", remote_side=id)
-
+
Suppose we wanted to add an accessor ``grandparent``. This would return the ``parent`` of
``Node.parent``. When we have an instance of ``Node``, this is simple::
@@ -431,7 +431,7 @@ Suppose we wanted to add an accessor ``grandparent``. This would return the ``p
class Node(Base):
# ...
-
+
@hybrid_property
def grandparent(self):
return self.parent.parent
@@ -460,7 +460,7 @@ attribute and filtered based on the given criterion::
id =Column(Integer, primary_key=True)
parent_id = Column(Integer, ForeignKey('node.id'))
parent = relationship("Node", remote_side=id)
-
+
@hybrid_property
def grandparent(self):
return self.parent.parent
@@ -486,8 +486,8 @@ using :attr:`.Operators.eq` against the left and right sides, passing into
{sql}>>> session.query(Node).\\
... with_transformation(Node.grandparent==Node(id=5)).\\
... all()
- SELECT node.id AS node_id, node.parent_id AS node_parent_id
- FROM node JOIN node AS node_1 ON node_1.id = node.parent_id
+ SELECT node.id AS node_id, node.parent_id AS node_parent_id
+ FROM node JOIN node AS node_1 ON node_1.id = node.parent_id
WHERE :param_1 = node_1.parent_id
{stop}
@@ -529,14 +529,14 @@ with each class::
{sql}>>> session.query(Node).\\
... with_transformation(Node.grandparent.join).\\
... filter(Node.grandparent==Node(id=5))
- SELECT node.id AS node_id, node.parent_id AS node_parent_id
- FROM node JOIN node AS node_1 ON node_1.id = node.parent_id
+ SELECT node.id AS node_id, node.parent_id AS node_parent_id
+ FROM node JOIN node AS node_1 ON node_1.id = node.parent_id
WHERE :param_1 = node_1.parent_id
{stop}
The "transformer" pattern is an experimental pattern that starts
to make usage of some functional programming paradigms.
-While it's only recommended for advanced and/or patient developers,
+While it's only recommended for advanced and/or patient developers,
there's probably a whole lot of amazing things it can be used for.
"""
@@ -546,26 +546,26 @@ from sqlalchemy.orm import attributes, interfaces
class hybrid_method(object):
"""A decorator which allows definition of a Python object method with both
instance-level and class-level behavior.
-
+
"""
def __init__(self, func, expr=None):
"""Create a new :class:`.hybrid_method`.
-
+
Usage is typically via decorator::
-
+
from sqlalchemy.ext.hybrid import hybrid_method
-
+
class SomeClass(object):
@hybrid_method
def value(self, x, y):
return self._value + x + y
-
+
@value.expression
def value(self, x, y):
return func.some_function(self._value, x, y)
-
+
"""
self.func = func
self.expr = expr or func
@@ -585,25 +585,25 @@ class hybrid_method(object):
class hybrid_property(object):
"""A decorator which allows definition of a Python descriptor with both
instance-level and class-level behavior.
-
+
"""
def __init__(self, fget, fset=None, fdel=None, expr=None):
"""Create a new :class:`.hybrid_property`.
-
+
Usage is typically via decorator::
-
+
from sqlalchemy.ext.hybrid import hybrid_property
-
+
class SomeClass(object):
@hybrid_property
def value(self):
return self._value
-
+
@value.setter
def value(self, value):
self._value = value
-
+
"""
self.fget = fget
self.fset = fset
@@ -647,10 +647,10 @@ class hybrid_property(object):
def comparator(self, comparator):
"""Provide a modifying decorator that defines a custom comparator producing method.
-
+
The return value of the decorated method should be an instance of
:class:`~.hybrid.Comparator`.
-
+
"""
proxy_attr = attributes.\
@@ -660,11 +660,11 @@ class hybrid_property(object):
self.expr = expr
return self
-
class Comparator(interfaces.PropComparator):
"""A helper class that allows easy construction of custom :class:`~.orm.interfaces.PropComparator`
classes for usage with hybrids."""
+ property = None
def __init__(self, expression):
self.expression = expression