[Sqlalchemy-tickets] Issue #4082: marking subclass of declared_attr with cascading ignores subclass
Brought to you by:
zzzeek
From: David L. <iss...@bi...> - 2017-09-18 16:16:00
|
New issue 4082: marking subclass of declared_attr with cascading ignores subclass https://bitbucket.org/zzzeek/sqlalchemy/issues/4082/marking-subclass-of-declared_attr-with David Lord: Before a class is mapped, I want to check if it defines a primary key. If the column is defined in a `declared_attr`, I can't know if it's primary without evaluating the attr, which SQLAlchemy correctly warns about, since the model is not mapped yet. To solve this, I thought I'd require marking declared attrs that define a primary key with a `declared_pkey` subclass. However, this breaks when using `declarded_pkey.cascading`, since internally `_stateful_declared_attr` creates a `declared_attr` instead of knowing about the subclass. I realize that `cascading` is not necessary in this contrived example, but it still demonstrates the issue. Running the following code should print 'pkey named id' but does not. ```python3 class declared_pkey(declared_attr): primary_key = True class IDMixin: @declared_pkey.cascading def id(cls): return Column(Integer, primary_key=True) class ExampleMeta(DeclarativeMeta): def __init__(cls, name, bases, d): for base in cls.__mro__: for key, value in base.__dict__.items(): if ( isinstance(value, (declared_attr, sa.Column, InstrumentedAttribute)) and getattr(value, 'primary_key', False) ): print(f'pkey named {key}') super(ExampleMeta, cls).__init__(name, bases, d) Base = declarative_base(metaclass=ExampleMeta) class User(IDMixin, Base): __tablename__ = 'user' ``` For now my solution has been to document that this doesn't work and tell people to do `id.primary_key = True` manually, but it would be nice if subclasses were preserved. ```python3 class IDMixin: @declared_attr.cascading def id(cls): return Column(Integer, primary_key=True) id.primary_key = True ``` To solve this, `_stateful_declared_attr` could be modifed to take the class as well. ```python3 class _stateful_declared_attr(declared_attr): def __init__(self, cls, **kw): self.cls = cls self.kw = kw def _stateful(self, **kw): new_kw = self.kw.copy() new_kw.update(kw) return _stateful_declared_attr(self.cls, **new_kw) def __call__(self, fn): return self.cls(fn, **self.kw) class declared_attr(...): ... def _stateful(cls, **kw): return _stateful_declared_attr(cls, **kw) ... ``` |