]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
It's now possible to map only a subset of available selectable columns onto mapper...
authorJason Kirtland <jek@discorporate.us>
Wed, 1 Aug 2007 01:44:24 +0000 (01:44 +0000)
committerJason Kirtland <jek@discorporate.us>
Wed, 1 Aug 2007 01:44:24 +0000 (01:44 +0000)
CHANGES
lib/sqlalchemy/orm/__init__.py
lib/sqlalchemy/orm/mapper.py
test/orm/mapper.py

diff --git a/CHANGES b/CHANGES
index dd9b1d138971f618211ac4a05dac81d8235d474f..516aa07a19e07831c35c1becef6b97af4f664847 100644 (file)
--- a/CHANGES
+++ b/CHANGES
           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".
       
index 3d8461dfd677826dcc9cd01a876f69f459cde9c0..66c4d796e695db23ba8756f852e29789e48b1609 100644 (file)
@@ -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
index c96b3be2223ee5b96041ac80f5ffd3ae0b1a44f6..6706b07e47cd3478b6126af9498da1d2ad523c61 100644 (file)
@@ -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)
index 414a1936fc7d9bee4d326e3137c688e6bbeef4d8..5bb33b2415263d8eacf1350e06305bb854c3fc50 100644 (file)
@@ -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"""