Issues

Issue #2517 resolved

honor non ORM/core constructs from @declared_attr

Mike Bayer
repo owner created an issue

association proxy is one target, could be others, e.g.::

from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.declarative import declared_attr

metadata = MetaData()
BaseObject = declarative_base(metadata=metadata)

class BaseFilter(BaseObject):
   __abstract__ = True

   id = Column(Integer(), primary_key=True, autoincrement=True)
   filter = Column(UnicodeText(), nullable=False)

   @declared_attr
   def __tablename__(cls):
       return '%s_filter' % cls.parent_tablename

   @declared_attr
   def parent_id(cls):
       return Column(Integer(),
           ForeignKey('%s.id' % cls.parent_tablename,
               ondelete='CASCADE', onupdate='CASCADE'),
           nullable=False, index=True)

   def __init__(self, filter, **kw):
       super(BaseFilter, self).__init__(filter=filter, **kw)

class FilterA(BaseFilter):
   parent_tablename = 'type_a'

class FilterB(BaseFilter):
   parent_tablename = 'type_b'

class FilterMixin(object):
    @declared_attr
    def _filters(cls):
       return relationship(cls.filter_class,
               cascade='all,delete,delete-orphan')

    @declared_attr
    def filters(cls):
        return association_proxy('_filters', 'filter')


class TypeA(BaseObject, FilterMixin):
   __tablename__ = 'type_a'
   filter_class = FilterA
   id = Column(Integer(), primary_key=True, autoincrement=True)

class TypeB(BaseObject, FilterMixin):
   __tablename__ = 'type_b'
   filter_class = FilterB
   id = Column(Integer(), primary_key=True, autoincrement=True)


engine = create_engine('sqlite://')
metadata.bind = engine
metadata.create_all()
Session = sessionmaker(bind=engine)
session = Session()

session.add(TypeA(filters=[u'foo'](u'foo')))
session.add(TypeB(filters=[u'foo'](u'foo')))
session.flush()

patch:

--- a/lib/sqlalchemy/ext/declarative.py Mon Jun 18 10:06:49 2012 -0400
+++ b/lib/sqlalchemy/ext/declarative.py Wed Jun 20 18:43:38 2012 -0400
@@ -1189,7 +1189,7 @@
     cls._decl_class_registry[classname](classname) = cls
     our_stuff = util.OrderedDict()

-    for k in dict_:
+    for k in list(dict_):

         # TODO: improve this ?  all dunders ?
         if k in ('__table__', '__tablename__', '__mapper_args__'):
@@ -1206,6 +1206,9 @@
                       "left at the end of the line?" % k)
             continue
         if not isinstance(value, (Column, MapperProperty)):
+            if not k.startswith('__'):
+                dict_.pop(k)
+                setattr(cls, k, value)
             continue
         if k == 'metadata':
             raise exc.InvalidRequestError(

Comments (2)

  1. Log in to comment