]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- Further reworked the "mixin" logic in declarative to
authorMike Bayer <mike_mp@zzzcomputing.com>
Tue, 6 Apr 2010 16:27:01 +0000 (12:27 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Tue, 6 Apr 2010 16:27:01 +0000 (12:27 -0400)
additionally allow __mapper_args__ as a @classproperty
on a mixin, such as to dynamically assign polymorphic_identity.

CHANGES
lib/sqlalchemy/ext/declarative.py
test/ext/test_declarative.py

diff --git a/CHANGES b/CHANGES
index 2b2b8bd346697f56a669cdafccf243ab415d1320..c12e213a50b9e3d22df57f3d9c6faad90c7f4e2c 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -76,6 +76,10 @@ CHANGES
     if a non-mapped class attribute is referenced in the
     string-based relationship() arguments.
 
+  - Further reworked the "mixin" logic in declarative to 
+    additionally allow __mapper_args__ as a @classproperty
+    on a mixin, such as to dynamically assign polymorphic_identity.
+    
 - oracle
   - Now using cx_oracle output converters so that the
     DBAPI returns natively the kinds of values we prefer:
index 407de100463c6c6a739be8727480ec1cbab1a569..94dba77faed664f48fca4aaab5a906efe0f3bf3f 100644 (file)
@@ -535,43 +535,46 @@ def _as_declarative(cls, classname, dict_):
     dict_ = dict(dict_)
 
     column_copies = dict()
-    unmapped_mixins = False
-    for base in cls.__bases__:
-        names = dir(base)
-        if not _is_mapped_class(base):
-            unmapped_mixins = True
-            for name in names:
-                obj = getattr(base,name, None)
-                if isinstance(obj, Column):
-                    if obj.foreign_keys:
-                        raise exceptions.InvalidRequestError(
-                            "Columns with foreign keys to other columns "
-                            "are not allowed on declarative mixins at this time."
-                        )
-                    dict_[name]=column_copies[obj]=obj.copy()
-                elif isinstance(obj, RelationshipProperty):
-                    raise exceptions.InvalidRequestError(
-                                        "relationships are not allowed on "
-                                        "declarative mixins at this time.")
-
-    # doing it this way enables these attributes to be descriptors
-    get_mapper_args = '__mapper_args__' in dict_
-    get_table_args = '__table_args__' in dict_
-    if unmapped_mixins:
-        get_mapper_args = get_mapper_args or getattr(cls,'__mapper_args__',None)
-        get_table_args = get_table_args or getattr(cls,'__table_args__',None)
-        tablename = getattr(cls,'__tablename__',None)
-        if tablename:
-            # subtle: if tablename is a descriptor here, we actually
-            # put the wrong value in, but it serves as a marker to get
-            # the right value value...
-            dict_['__tablename__']=tablename
-
-    # now that we know whether or not to get these, get them from the class
-    # if we should, enabling them to be decorators
-    mapper_args = get_mapper_args and cls.__mapper_args__ or {}
-    table_args = get_table_args and cls.__table_args__ or None
+    mixin_table_args = None
+    mapper_args = {}
+    table_args = None
     
+    def _is_mixin(klass):
+        return not _is_mapped_class(klass) and klass is not cls
+    
+    for base in cls.__mro__:
+        if _is_mixin(base):
+            for name in dir(base):
+                if name == '__mapper_args__':
+                    if not mapper_args:
+                        mapper_args = cls.__mapper_args__
+                elif name == '__table_args__':
+                    if not table_args:
+                        table_args = mixin_table_args = cls.__table_args__
+                elif name == '__tablename__':
+                    if '__tablename__' not in dict_:
+                        dict_['__tablename__'] = cls.__tablename__
+                else:
+                    obj = getattr(base,name, None)
+                    if isinstance(obj, Column):
+                        if obj.foreign_keys:
+                            raise exceptions.InvalidRequestError(
+                                "Columns with foreign keys to other columns "
+                                "are not allowed on declarative mixins at this time."
+                            )
+                        dict_[name]=column_copies[obj]=obj.copy()
+                    elif isinstance(obj, RelationshipProperty):
+                        raise exceptions.InvalidRequestError(
+                                            "relationships are not allowed on "
+                                            "declarative mixins at this time.")
+        elif base is cls:
+            if '__mapper_args__' in dict_:
+                mapper_args = cls.__mapper_args__
+            if '__table_args__' in dict_:
+                table_args = cls.__table_args__
+            if '__tablename__' in dict_:
+                dict_['__tablename__'] = cls.__tablename__
+                
     # make sure that column copies are used rather than the original columns
     # from any mixins
     for k, v in mapper_args.iteritems():
@@ -681,7 +684,7 @@ def _as_declarative(cls, classname, dict_):
         if table is None:
             # single table inheritance.
             # ensure no table args
-            if table_args is not None:
+            if table_args is not None and table_args is not mixin_table_args:
                 raise exceptions.ArgumentError(
                     "Can't place __table_args__ on an inherited class with no table."
                     )
index 0b1f74f67a979ff4e254d266b845df0285bc05db..b0140fb4207d18ac7e6875593f7bf2cb0e135352 100644 (file)
@@ -2052,7 +2052,29 @@ class DeclarativeMixinTest(DeclarativeTestBase):
             id =  Column(Integer, primary_key=True)
 
         eq_(MyModel.__table__.kwargs,{'mysql_engine': 'InnoDB'})
+    
+    def test_mapper_args_classproperty(self):
+        class ComputedMapperArgs:
+            @classproperty
+            def __mapper_args__(cls):
+                if cls.__name__=='Person':
+                    return dict(polymorphic_on=cls.discriminator)
+                else:
+                    return dict(polymorphic_identity=cls.__name__)
+
+        class Person(Base,ComputedMapperArgs):
+            __tablename__ = 'people'
+            id = Column(Integer, primary_key=True)
+            discriminator = Column('type', String(50))
+
+        class Engineer(Person):
+            pass
 
+        compile_mappers()
+
+        assert class_mapper(Person).polymorphic_on is Person.__table__.c.type
+        eq_(class_mapper(Engineer).polymorphic_identity, 'Engineer')
+        
     def test_table_args_composite(self):
 
         class MyMixin1: