summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2007-04-29 22:26:39 +0000
committerMike Bayer <mike_mp@zzzcomputing.com>2007-04-29 22:26:39 +0000
commit146e0515d8c8f991857fb3cf27a6b9a5583d25d1 (patch)
tree4948f1b6f1efae8a8678d7834b335ff13a6b5140 /lib/sqlalchemy
parent8d5a0729abff9fe4ac86f49d810564037a68b0e3 (diff)
downloadsqlalchemy-146e0515d8c8f991857fb3cf27a6b9a5583d25d1.tar.gz
- restored old "column_property()" ORM function (used to be called
"column()") to force any column expression to be added as a property on a mapper, particularly those that aren't present in the mapped selectable. this allows "scalar expressions" of any kind to be added as relations (though they have issues with eager loads).
Diffstat (limited to 'lib/sqlalchemy')
-rw-r--r--lib/sqlalchemy/orm/__init__.py29
-rw-r--r--lib/sqlalchemy/orm/mapper.py33
-rw-r--r--lib/sqlalchemy/orm/strategies.py6
-rw-r--r--lib/sqlalchemy/sql.py2
4 files changed, 46 insertions, 24 deletions
diff --git a/lib/sqlalchemy/orm/__init__.py b/lib/sqlalchemy/orm/__init__.py
index 48b69dadf..7a73ecca5 100644
--- a/lib/sqlalchemy/orm/__init__.py
+++ b/lib/sqlalchemy/orm/__init__.py
@@ -19,7 +19,7 @@ from sqlalchemy.orm import properties, strategies, interfaces
from sqlalchemy.orm.session import Session as create_session
from sqlalchemy.orm.session import object_session, attribute_manager
-__all__ = ['relation', 'backref', 'eagerload', 'lazyload', 'noload', 'deferred', 'defer', 'undefer', 'extension',
+__all__ = ['relation', 'column_property', 'backref', 'eagerload', 'lazyload', 'noload', 'deferred', 'defer', 'undefer', 'extension',
'mapper', 'clear_mappers', 'compile_mappers', 'clear_mapper', 'class_mapper', 'object_mapper', 'MapperExtension', 'Query',
'cascade_mappers', 'polymorphic_union', 'create_session', 'synonym', 'contains_alias', 'contains_eager', 'EXT_PASS', 'object_session'
]
@@ -34,6 +34,33 @@ def relation(*args, **kwargs):
raise exceptions.ArgumentError("relation(class, table, **kwargs) is deprecated. Please use relation(class, **kwargs) or relation(mapper, **kwargs).")
return _relation_loader(*args, **kwargs)
+def column_property(*args, **kwargs):
+ """Provide a column-level property for use with a Mapper.
+
+ Normally, custom column-level properties that represent columns
+ directly or indirectly present within the mapped selectable
+ can just be added to the ``properties`` dictionary directly,
+ in which case this function's usage is not necessary.
+
+ In the case of a ``ColumnElement`` directly present within the
+ ``properties`` dictionary, the given column is converted to be the exact column
+ located within the mapped selectable, in the case that the mapped selectable
+ is not the exact parent selectable of the given column, but shares a common
+ base table relationship with that column.
+
+ Use this function when the column expression being added does not
+ correspond to any single column within the mapped selectable,
+ such as a labeled function or scalar-returning subquery, to force the element
+ to become a mapped property regardless of it not being present within the
+ mapped selectable.
+
+ Note that persistence of instances is driven from the collection of columns
+ within the mapped selectable, so column properties attached to a Mapper which have
+ no direct correspondence to the mapped selectable will effectively be non-persisted
+ attributes.
+ """
+ return properties.ColumnProperty(*args, **kwargs)
+
def _relation_loader(mapper, secondary=None, primaryjoin=None, secondaryjoin=None, lazy=True, **kwargs):
return properties.PropertyLoader(mapper, secondary, primaryjoin, secondaryjoin, lazy=lazy, **kwargs)
diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py
index ab90f24b6..af9d53ac9 100644
--- a/lib/sqlalchemy/orm/mapper.py
+++ b/lib/sqlalchemy/orm/mapper.py
@@ -792,33 +792,24 @@ class Mapper(object):
self._compile_all()
self._compile_property(key, prop, init=True)
- def _create_prop_from_column(self, column, skipmissing=False):
- if sql.is_column(column):
- try:
- column = self.mapped_table.corresponding_column(column)
- except KeyError:
- if skipmissing:
- return
- raise exceptions.ArgumentError("Column '%s' is not represented in mapper's table" % prop._label)
- return ColumnProperty(column)
- elif isinstance(column, list) and sql.is_column(column[0]):
- try:
- column = [self.mapped_table.corresponding_column(c) for c in column]
- except KeyError, e:
- # TODO: want to take the columns we have from this
- if skipmissing:
- return
- raise exceptions.ArgumentError("Column '%s' is not represented in mapper's table" % e.args[0])
- return ColumnProperty(*column)
- else:
+ def _create_prop_from_column(self, column):
+ column = util.to_list(column)
+ if not sql.is_column(column[0]):
return None
+ mapped_column = []
+ for c in column:
+ mc = self.mapped_table.corresponding_column(c, raiseerr=False)
+ if not mc:
+ raise exceptions.ArgumentError("Column '%s' is not represented in mapper's table. Use the `column_property()` function to force this column to be mapped as a read-only attribute." % str(c))
+ mapped_column.append(mc)
+ return ColumnProperty(*mapped_column)
def _adapt_inherited_property(self, key, prop):
if not self.concrete:
self._compile_property(key, prop, init=False, setparent=False)
# TODO: concrete properties dont adapt at all right now....will require copies of relations() etc.
- def _compile_property(self, key, prop, init=True, skipmissing=False, setparent=True):
+ def _compile_property(self, key, prop, init=True, setparent=True):
"""Add a ``MapperProperty`` to this or another ``Mapper``,
including configuration of the property.
@@ -833,7 +824,7 @@ class Mapper(object):
self.__log("_compile_property(%s, %s)" % (key, prop.__class__.__name__))
if not isinstance(prop, MapperProperty):
- col = self._create_prop_from_column(prop, skipmissing=skipmissing)
+ col = self._create_prop_from_column(prop)
if col is None:
raise exceptions.ArgumentError("%s=%r is not an instance of MapperProperty or Column" % (key, prop))
prop = col
diff --git a/lib/sqlalchemy/orm/strategies.py b/lib/sqlalchemy/orm/strategies.py
index cdf71e044..1d7eee56d 100644
--- a/lib/sqlalchemy/orm/strategies.py
+++ b/lib/sqlalchemy/orm/strategies.py
@@ -23,7 +23,11 @@ class ColumnLoader(LoaderStrategy):
def setup_query(self, context, eagertable=None, **kwargs):
for c in self.columns:
if eagertable is not None:
- context.statement.append_column(eagertable.corresponding_column(c))
+ conv = eagertable.corresponding_column(c, raiseerr=False)
+ if conv:
+ context.statement.append_column(conv)
+ else:
+ context.statement.append_column(c)
else:
context.statement.append_column(c)
diff --git a/lib/sqlalchemy/sql.py b/lib/sqlalchemy/sql.py
index a7ca55e31..6858084de 100644
--- a/lib/sqlalchemy/sql.py
+++ b/lib/sqlalchemy/sql.py
@@ -1617,7 +1617,7 @@ class FromClause(Selectable):
if not raiseerr:
return None
else:
- raise exceptions.InvalidRequestError("Given column '%s', attached to table '%s', failed to locate a corresponding column from table '%s'" % (str(column), str(column.table), self.name))
+ raise exceptions.InvalidRequestError("Given column '%s', attached to table '%s', failed to locate a corresponding column from table '%s'" % (str(column), str(getattr(column, 'table', None)), self.name))
def _get_exported_attribute(self, name):
try: