declarative classproperty...still broken (need __mapper_args__ etc. to work from non-mixins)

Issue #1922 resolved
Mike Bayer repo owner created an issue

we ignore __mapper_args__, __table_args__ from the base when processing the subclass:

from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.util import classproperty

Base = declarative_base()

class Car(Base):
   __tablename__ = 'car'
   id = Column(Integer, primary_key=True)
   class_name = Column(Unicode(128), nullable=False)

   @classproperty
   def __mapper_args__(cls):
       if cls.__name__ == "Car":
           return {"polymorphic_on":cls.class_name, "polymorphic_identity":"Car", "with_polymorphic":"*"}
       else:
           return {"polymorphic_identity":cls.__name__}

class Ferrari(Car):
    pass

# fails
assert class_mapper(Ferrari).polymorphic_identity == "Ferrari"

Since we are going with the notion that @classproperty is special to declarative (and replacing with @mapperproperty in 0.7), this patch adds that approach to the mapper and table elements:

diff -r c5c8cdf3b4d7dc456cfef29ea04b2b7300060c7a lib/sqlalchemy/ext/declarative.py
--- a/lib/sqlalchemy/ext/declarative.py Sat Sep 18 13:34:04 2010 -0400
+++ b/lib/sqlalchemy/ext/declarative.py Mon Sep 20 10:43:34 2010 -0400
@@ -908,51 +908,63 @@
     parent_columns = ()

     for base in cls.__mro__:
-        if _is_mapped_class(base):
+        class_mapped = _is_mapped_class(base)
+        if class_mapped:
             parent_columns = base.__table__.c.keys()
-        else:
-            for name,obj in vars(base).items():
-                if name == '__mapper_args__':
-                    if not mapper_args:
-                        mapper_args = cls.__mapper_args__
-                elif name == '__tablename__':
-                    if not tablename:
-                        tablename = cls.__tablename__
-                elif name == '__table_args__':
-                    if not table_args:                        
-                        table_args = cls.__table_args__
-                        if base is not cls:
-                            inherited_table_args = True
-                elif base is not cls:
-                    # we're a mixin.
-                    
-                    if isinstance(obj, Column):
-                        if obj.foreign_keys:
-                            raise exceptions.InvalidRequestError(
-                            "Columns with foreign keys to other columns "
-                            "must be declared as @classproperty callables "
-                            "on declarative mixin classes. ")
-                        if name not in dict_ and not (
-                                '__table__' in dict_ and 
-                                name in dict_['__table__']('__table__').c
-                                ):
-                            potential_columns[name](name) = \
-                                    column_copies[obj](obj) = \
-                                    obj.copy()
-                            column_copies[obj](obj)._creation_order = \
-                                    obj._creation_order
-                    elif isinstance(obj, MapperProperty):
+            
+        for name,obj in vars(base).items():
+            if name == '__mapper_args__':
+                if not mapper_args and (
+                                        not class_mapped or 
+                                        isinstance(obj, util.classproperty)
+                                    ):
+                    mapper_args = cls.__mapper_args__
+            elif name == '__tablename__':
+                if not tablename and (
+                                        not class_mapped or 
+                                        isinstance(obj, util.classproperty)
+                                    ):
+                    tablename = cls.__tablename__
+            elif name == '__table_args__':
+                if not table_args and (
+                                        not class_mapped or 
+                                        isinstance(obj, util.classproperty)
+                                    ):
+                    table_args = cls.__table_args__
+                    if base is not cls:
+                        inherited_table_args = True
+            elif class_mapped:
+                continue
+            elif base is not cls:
+                # we're a mixin.
+                
+                if isinstance(obj, Column):
+                    if obj.foreign_keys:
                         raise exceptions.InvalidRequestError(
-                            "Mapper properties (i.e. deferred,"
-                            "column_property(), relationship(), etc.) must "
-                            "be declared as @classproperty callables "
-                            "on declarative mixin classes.")
-                    elif isinstance(obj, util.classproperty):
-                        dict_[name](name) = ret = \
-                                column_copies[obj](obj) = getattr(cls, name)
-                        if isinstance(ret, (Column, MapperProperty)) and \
-                            ret.doc is None:
-                            ret.doc = obj.__doc__
+                        "Columns with foreign keys to other columns "
+                        "must be declared as @classproperty callables "
+                        "on declarative mixin classes. ")
+                    if name not in dict_ and not (
+                            '__table__' in dict_ and 
+                            name in dict_['__table__']('__table__').c
+                            ):
+                        potential_columns[name](name) = \
+                                column_copies[obj](obj) = \
+                                obj.copy()
+                        column_copies[obj](obj)._creation_order = \
+                                obj._creation_order
+                elif isinstance(obj, MapperProperty):
+                    raise exceptions.InvalidRequestError(
+                        "Mapper properties (i.e. deferred,"
+                        "column_property(), relationship(), etc.) must "
+                        "be declared as @classproperty callables "
+                        "on declarative mixin classes.")
+                elif isinstance(obj, util.classproperty):
+                    dict_[name](name) = ret = \
+                            column_copies[obj](obj) = getattr(cls, name)
+                    if isinstance(ret, (Column, MapperProperty)) and \
+                        ret.doc is None:
+                        ret.doc = obj.__doc__

     # apply inherited columns as we should
     for k, v in potential_columns.items():

Comments (2)

  1. Log in to comment