can we get subclass mappers to self-collect?

Issue #2526 resolved
Mike Bayer repo owner created an issue

would need a lot of weak references for this to work automatically, particularly the memoizations somehow, which would add some real performance overhead:

diff -r 45007b9c2effe931ca4493b25171fb9a9c244611 lib/sqlalchemy/ext/declarative.py
--- a/lib/sqlalchemy/ext/declarative.py Mon Jun 25 16:42:39 2012 -0400
+++ b/lib/sqlalchemy/ext/declarative.py Tue Jun 26 12:58:56 2012 -0400
@@ -1036,7 +1036,7 @@
 from sqlalchemy.sql import util as sql_util, expression
 from sqlalchemy import event
 from sqlalchemy.orm.util import polymorphic_union, _mapper_or_none
-
+import weakref

 __all__ = 'declarative_base', 'synonym_for', \
             'comparable_using', 'instrument_declarative'
@@ -1718,7 +1718,7 @@
         lcl_metadata.bind = bind

     if class_registry is None:
-        class_registry = {}
+        class_registry = weakref.WeakValueDictionary()

     bases = not isinstance(cls, tuple) and (cls,) or cls
     class_dict = dict(_decl_class_registry=class_registry,
diff -r 45007b9c2effe931ca4493b25171fb9a9c244611 lib/sqlalchemy/orm/mapper.py
--- a/lib/sqlalchemy/orm/mapper.py  Mon Jun 25 16:42:39 2012 -0400
+++ b/lib/sqlalchemy/orm/mapper.py  Tue Jun 26 12:58:56 2012 -0400
@@ -166,7 +166,7 @@
         # names with Mappers that will be used to construct object instances
         # upon a select operation.
         if _polymorphic_map is None:
-            self.polymorphic_map = {}
+            self.polymorphic_map = weakref.WeakValueDictionary()
         else:
             self.polymorphic_map = _polymorphic_map

@@ -432,7 +432,8 @@
         being present."""

         # a set of all mappers which inherit from this one.
-        self._inheriting_mappers = set()
+        # TODO: WeakSet is only in 2.7, would need compat
+        self._inheriting_mappers = weakref.WeakSet()

         if self.inherits:
             if isinstance(self.inherits, type):



#!python
from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base

Base= declarative_base()

class Root(Base):
    __tablename__ = 'root'
    id = Column(Integer, primary_key=True)
    type = Column(String)
    __mapper_args__ = {
        'polymorphic_on':type,
        'polymorphic_identity':'root'
    }

assert not Root.__mapper__._inheriting_mappers

class Sub(Root):
    __tablename__ = 'sub'
    metadata = MetaData()
    id = Column(Integer, ForeignKey(Root.id), primary_key=True)
    __mapper_args__ = {
        'polymorphic_identity':'sub'
    }

e = create_engine("sqlite://")
Base.metadata.create_all(e)
Sub.metadata.create_all(e)
compile_mappers()
s = Session(e)

s.add_all([Sub()](Root(),))
print s.query(Root).all()

del Sub

# this step is required, though, unless we 
# make the memoizations weak too, that would
# be a real performance hit
Root.__mapper__._expire_memoizations()

import gc
gc.collect()
assert not Root.__mapper__._inheriting_mappers

Comments (4)

  1. Mike Bayer reporter

    6dad6332cd0b777e4d876f51fada4fdf31299c53 patches just the declarative part. The rest of it is really not worth it - the attached patch illustrates getting it to work for single table inheritance but not yet joined, which already includes many new weak collections. I'd rather not add all that overhead for this extremely rare and arguably unnecessary use case.

  2. Log in to comment