diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2010-08-02 15:29:31 -0400 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2010-08-02 15:29:31 -0400 |
| commit | e616c2fb3cbc1f2fb7102f3fa666439c688e48b7 (patch) | |
| tree | 7b635c217af1bf675956dbfd9ce27a679249060c /lib | |
| parent | b2c0b50bbfa43f662afd16b7ca51bcfe17e4c351 (diff) | |
| download | sqlalchemy-e616c2fb3cbc1f2fb7102f3fa666439c688e48b7.tar.gz | |
- if @classproperty is used with a regular class-bound
mapper property attribute, it will be called to get the
actual attribute value during initialization. Currently,
there's no advantage to using @classproperty on a column
or relationship attribute of a declarative class that
isn't a mixin - evaluation is at the same time as if
@classproperty weren't used. But here we at least allow
it to function as expected.
- docs for column_property() with declarative
- mixin docs in declarative made more clear - mixins
are optional - each subsection starts with, "in *declarative mixins*",
to reduce confusion
Diffstat (limited to 'lib')
| -rwxr-xr-x | lib/sqlalchemy/ext/declarative.py | 97 | ||||
| -rw-r--r-- | lib/sqlalchemy/sql/expression.py | 2 |
2 files changed, 82 insertions, 17 deletions
diff --git a/lib/sqlalchemy/ext/declarative.py b/lib/sqlalchemy/ext/declarative.py index 3370a764c..7f9dacbb0 100755 --- a/lib/sqlalchemy/ext/declarative.py +++ b/lib/sqlalchemy/ext/declarative.py @@ -233,6 +233,61 @@ Similarly, :func:`comparable_using` is a front end for the def uc_name(self): return self.name.upper() +.. _declarative_sql_expressions: + +Defining SQL Expressions +======================== + +The usage of :func:`.column_property` with Declarative is +pretty much the same as that described in +:ref:`mapper_sql_expressions`. Local columns within the same +class declaration can be referenced directly:: + + class User(Base): + __tablename__ = 'user' + id = Column(Integer, primary_key=True) + firstname = Column(String) + lastname = Column(String) + fullname = column_property( + firstname + " " + lastname + ) + +Correlated subqueries reference the :class:`Column` objects they +need either from the local class definition or from remote +classes:: + + from sqlalchemy.sql import func + + class Address(Base): + __tablename__ = 'address' + + id = Column('id', Integer, primary_key=True) + user_id = Column(Integer, ForeignKey('user.id')) + + class User(Base): + __tablename__ = 'user' + + id = Column(Integer, primary_key=True) + name = Column(String) + + address_count = column_property( + select([func.count(Address.id)]).\\ + where(Address.user_id==id) + ) + +In the case that the ``address_count`` attribute above doesn't have access to +``Address`` when ``User`` is defined, the ``address_count`` attribute should +be added to ``User`` when both ``User`` and ``Address`` are available (i.e. +there is no string based "late compilation" feature like there is with +:func:`.relationship` at this time). Note we reference the ``id`` column +attribute of ``User`` with its class when we are no longer in the declaration +of the ``User`` class:: + + User.address_count = column_property( + select([func.count(Address.id)]).\\ + where(Address.user_id==User.id) + ) + Table Configuration =================== @@ -429,6 +484,11 @@ share some functionality, often a set of columns, across many classes. The normal Python idiom would be to put this common code into a base class and have all the other classes subclass this class. +.. note:: Mixins are an entirely optional feature when using declarative, + and are not required for any configuration. Users who don't need + to define sets of attributes common among many classes can + skip this section. + When using :mod:`~sqlalchemy.ext.declarative`, this need is met by using a "mixin class". A mixin class is one that isn't mapped to a table and doesn't subclass the declarative :class:`Base`. For example:: @@ -531,12 +591,12 @@ Mixing in Relationships ~~~~~~~~~~~~~~~~~~~~~~~ Relationships created by :func:`~sqlalchemy.orm.relationship` are provided -exclusively using the :func:`~sqlalchemy.util.classproperty` approach, -eliminating any ambiguity which could arise when copying a relationship -and its possibly column-bound contents. Below is an example which -combines a foreign key column and a relationship so that two classes -``Foo`` and ``Bar`` can both be configured to reference a common -target class via many-to-one:: +with declarative mixin classes exclusively using the +:func:`~sqlalchemy.util.classproperty` approach, eliminating any ambiguity +which could arise when copying a relationship and its possibly column-bound +contents. Below is an example which combines a foreign key column and a +relationship so that two classes ``Foo`` and ``Bar`` can both be configured to +reference a common target class via many-to-one:: class RefTargetMixin(object): @classproperty @@ -586,9 +646,9 @@ 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 have the -:func:`~sqlalchemy.util.classproperty` requirement so that no reliance on -copying is needed:: +etc. ultimately involve references to columns, and therefore, when +used with declarative mixins, have the :func:`~sqlalchemy.util.classproperty` +requirement so that no reliance on copying is needed:: class SomethingMixin(object): @@ -607,7 +667,8 @@ Controlling table inheritance with mixins ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The ``__tablename__`` attribute in conjunction with the hierarchy of -the classes involved controls what type of table inheritance, if any, +classes involved in a declarative mixin scenario controls what type of +table inheritance, if any, is configured by the declarative extension. If the ``__tablename__`` is computed by a mixin, you may need to @@ -700,12 +761,13 @@ classes:: Combining Table/Mapper Arguments from Multiple Mixins ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -In the case of ``__table_args__`` or ``__mapper_args__``, you may want -to combine some parameters from several mixins with those you wish to -define on the class iteself. The -:func:`~sqlalchemy.util.classproperty` decorator can be used here -to create user-defined collation routines that pull from multiple -collections:: +In the case of ``__table_args__`` or ``__mapper_args__`` +specified with declarative mixins, you may want to combine +some parameters from several mixins with those you wish to +define on the class iteself. The +:func:`~sqlalchemy.util.classproperty` decorator can be used +here to create user-defined collation routines that pull +from multiple collections:: from sqlalchemy.util import classproperty @@ -868,6 +930,9 @@ def _as_declarative(cls, classname, dict_): our_stuff = util.OrderedDict() for k in dict_: value = dict_[k] + if isinstance(value, util.classproperty): + value = getattr(cls, k) + if (isinstance(value, tuple) and len(value) == 1 and isinstance(value[0], (Column, MapperProperty))): util.warn("Ignoring declarative-like tuple value of attribute " diff --git a/lib/sqlalchemy/sql/expression.py b/lib/sqlalchemy/sql/expression.py index 050b5c05b..96147a94a 100644 --- a/lib/sqlalchemy/sql/expression.py +++ b/lib/sqlalchemy/sql/expression.py @@ -1813,7 +1813,7 @@ class ColumnElement(ClauseElement, _CompareMixin): else: name = str(self) co = ColumnClause(self.anon_label, selectable, type_=getattr(self, 'type', None)) - + co.proxies = [self] selectable.columns[name] = co return co |
