summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Kirtland <jek@discorporate.us>2007-08-01 01:44:24 +0000
committerJason Kirtland <jek@discorporate.us>2007-08-01 01:44:24 +0000
commitcd2176d48d3996dabbc44205ff7cee344b30248f (patch)
tree7b37273ae649006b8ba807d843c55b2514d9d7a2
parented6e7c3896ff33db876e15b3f20bc8494af74a7a (diff)
downloadsqlalchemy-cd2176d48d3996dabbc44205ff7cee344b30248f.tar.gz
It's now possible to map only a subset of available selectable columns onto mapper properties [ticket:696].
-rw-r--r--CHANGES3
-rw-r--r--lib/sqlalchemy/orm/__init__.py14
-rw-r--r--lib/sqlalchemy/orm/mapper.py60
-rw-r--r--test/orm/mapper.py51
4 files changed, 105 insertions, 23 deletions
diff --git a/CHANGES b/CHANGES
index dd9b1d138..516aa07a1 100644
--- a/CHANGES
+++ b/CHANGES
@@ -132,6 +132,9 @@
a column- level "deferred" load, via the "polymorphic_fetch"
argument, which can be set to 'select' or 'deferred'
+ - it's now possible to map only a subset of available selectable columns
+ onto mapper properties [ticket:696].
+
- added undefer_group() MapperOption, sets a set of "deferred"
columns joined by a "group" to load as "undeferred".
diff --git a/lib/sqlalchemy/orm/__init__.py b/lib/sqlalchemy/orm/__init__.py
index 3d8461dfd..66c4d796e 100644
--- a/lib/sqlalchemy/orm/__init__.py
+++ b/lib/sqlalchemy/orm/__init__.py
@@ -429,6 +429,20 @@ def mapper(class_, local_table=None, *args, **params):
each ``Column`` (although they can be overridden using this
dictionary).
+ include_properties
+ An inclusive list of properties to map. Columns present in the
+ mapped table but not present in this list will not be automatically
+ converted into properties.
+
+ exclude_properties
+ A list of properties not to map. Columns present in the
+ mapped table and present in this list will not be automatically
+ converted into properties. Note that neither this option nor
+ include_properties will allow an end-run around Python inheritance.
+ If mapped class ``B`` inherits from mapped class ``A``, no combination
+ of includes or excludes will allow ``B`` to have fewer properties
+ than its superclass, ``A``.
+
primary_key
A list of ``Column`` objects which define the *primary key*
to be used against this mapper's selectable unit. This is
diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py
index c96b3be22..6706b07e4 100644
--- a/lib/sqlalchemy/orm/mapper.py
+++ b/lib/sqlalchemy/orm/mapper.py
@@ -41,28 +41,30 @@ class Mapper(object):
"""
def __init__(self,
- class_,
- local_table,
- properties = None,
- primary_key = None,
- non_primary = False,
- inherits = None,
- inherit_condition = None,
- extension = None,
- order_by = False,
- allow_column_override = False,
- entity_name = None,
- always_refresh = False,
- version_id_col = None,
- polymorphic_on=None,
- _polymorphic_map=None,
- polymorphic_identity=None,
- polymorphic_fetch=None,
- concrete=False,
- select_table=None,
- allow_null_pks=False,
- batch=True,
- column_prefix=None):
+ class_,
+ local_table,
+ properties = None,
+ primary_key = None,
+ non_primary = False,
+ inherits = None,
+ inherit_condition = None,
+ extension = None,
+ order_by = False,
+ allow_column_override = False,
+ entity_name = None,
+ always_refresh = False,
+ version_id_col = None,
+ polymorphic_on=None,
+ _polymorphic_map=None,
+ polymorphic_identity=None,
+ polymorphic_fetch=None,
+ concrete=False,
+ select_table=None,
+ allow_null_pks=False,
+ batch=True,
+ column_prefix=None,
+ include_properties=None,
+ exclude_properties=None):
"""Construct a new mapper.
Mappers are normally constructed via the [sqlalchemy.orm#mapper()]
@@ -137,6 +139,9 @@ class Mapper(object):
self.columns = LOrderedProp()
self.c = self.columns
+ self.include_properties = include_properties
+ self.exclude_properties = exclude_properties
+
# each time the options() method is called, the resulting Mapper is
# stored in this dictionary based on the given options for fast re-access
self._options = {}
@@ -577,7 +582,18 @@ class Mapper(object):
column_key = (self.column_prefix or '') + column.key
prop = self.__props.get(column.key, None)
+
if prop is None:
+ if (self.include_properties is not None and
+ column_key not in self.include_properties):
+ self.__log("not including property %s" % (column_key))
+ continue
+
+ if (self.exclude_properties is not None and
+ column_key in self.exclude_properties):
+ self.__log("excluding property %s" % (column_key))
+ continue
+
prop = ColumnProperty(column)
self.__props[column_key] = prop
prop.set_parent(self)
diff --git a/test/orm/mapper.py b/test/orm/mapper.py
index 414a1936f..5bb33b241 100644
--- a/test/orm/mapper.py
+++ b/test/orm/mapper.py
@@ -241,7 +241,56 @@ class MapperTest(MapperSuperTest):
'addresses' : relation(mapper(Address, addresses))
}).compile()
self.assert_(User.addresses.property is m.get_property('addresses'))
-
+
+ def testpropfilters(self):
+ t = Table('person', MetaData(),
+ Column('id', Integer, primary_key=True),
+ Column('type', String),
+ Column('name', String),
+ Column('employee_number', Integer),
+ Column('boss_id', Integer, ForeignKey('person.id')),
+ Column('vendor_id', Integer))
+
+ class Person(object): pass
+ class Vendor(Person): pass
+ class Employee(Person): pass
+ class Manager(Employee): pass
+ class Hoho(object): pass
+ class Lala(object): pass
+
+ p_m = mapper(Person, t, polymorphic_on=t.c.type,
+ include_properties=('id', 'type', 'name'))
+ e_m = mapper(Employee, inherits=p_m, polymorphic_identity='employee',
+ properties={
+ 'boss': relation(Manager, backref='peon')
+ },
+ exclude_properties=('vendor_id',))
+
+ m_m = mapper(Manager, inherits=e_m, polymorphic_identity='manager',
+ include_properties=())
+
+ v_m = mapper(Vendor, inherits=p_m, polymorphic_identity='vendor',
+ exclude_properties=('boss_id', 'employee_number'))
+ h_m = mapper(Hoho, t, include_properties=('id', 'type', 'name'))
+ l_m = mapper(Lala, t, exclude_properties=('vendor_id', 'boss_id'))
+
+ for m in p_m, e_m, m_m, v_m, h_m, l_m:
+ m.compile()
+
+ def assert_props(cls, want):
+ have = set([n for n in dir(cls) if not n.startswith('_')])
+ want = set(want)
+ want.add('c')
+ self.assert_(have == want)
+
+ assert_props(Person, ['id', 'name', 'type'])
+ assert_props(Employee, ['boss', 'boss_id', 'employee_number',
+ 'id', 'name', 'type'])
+ assert_props(Manager, ['boss', 'boss_id', 'employee_number', 'peon',
+ 'id', 'name', 'type'])
+ assert_props(Vendor, ['vendor_id', 'id', 'name', 'type'])
+ assert_props(Hoho, ['id', 'name', 'type'])
+ assert_props(Lala, ['employee_number', 'id', 'name', 'type'])
def testrecursiveselectby(self):
"""test that no endless loop occurs when traversing for select_by"""