--- /dev/null
+.. change::
+ :tags: bug, orm, declarative, regression
+ :tickets: 6054
+
+ Fixed bug where user-mapped classes that contained an attribute named
+ "registry" would cause conflicts with the new registry-based mapping system
+ when using :class:`.DeclarativeMeta`. While the attribute remains
+ something that can be set explicitly on a declarative base to be
+ consumed by the metaclass, once located it is placed under a private
+ class variable so it does not conflict with future subclasses that use
+ the same name for other purposes.
+
+
from .decl_base import _del_attribute
from .decl_base import _mapper
from .descriptor_props import SynonymProperty as _orm_synonym
+from .. import exc
from .. import inspection
from .. import util
from ..sql.schema import MetaData
class DeclarativeMeta(type):
def __init__(cls, classname, bases, dict_, **kw):
+ # early-consume registry from the initial declarative base,
+ # assign privately to not conflict with subclass attributes named
+ # "registry"
+ reg = getattr(cls, "_sa_registry", None)
+ if reg is None:
+ reg = dict_.get("registry", None)
+ if not isinstance(reg, registry):
+ raise exc.InvalidRequestError(
+ "Declarative base class has no 'registry' attribute, "
+ "or registry is not a sqlalchemy.orm.registry() object"
+ )
+ else:
+ cls._sa_registry = reg
+
if not cls.__dict__.get("__abstract__", False):
- _as_declarative(cls.registry, cls, cls.__dict__)
+ _as_declarative(reg, cls, dict_)
type.__init__(cls, classname, bases, dict_)
def __setattr__(cls, key, value):
set([Child.name_upper.property.columns[0]]),
)
+ def test_class_has_registry_attr(self):
+ existing_registry = Base.registry
+
+ class A(Base):
+ __tablename__ = "a"
+
+ registry = {"foo": "bar"}
+ id = Column(Integer, primary_key=True)
+ data = Column(String)
+
+ class SubA(A):
+ pass
+
+ is_(Base.registry, existing_registry)
+ is_(inspect(A).registry, existing_registry)
+ eq_(A.registry, {"foo": "bar"})
+
+ is_(inspect(SubA).registry, existing_registry)
+ eq_(SubA.registry, {"foo": "bar"})
+
+ def test_class_does_not_have_registry_attr(self):
+ with assertions.expect_raises_message(
+ exc.InvalidRequestError,
+ r"Declarative base class has no 'registry' attribute, or "
+ r"registry is not a sqlalchemy.orm.registry\(\) object",
+ ):
+
+ class Base(with_metaclass(DeclarativeMeta)):
+ metadata = sa.MetaData()
+
def test_shared_class_registry(self):
reg = {}
Base1 = declarative_base(testing.db, class_registry=reg)