[Sqlalchemy-tickets] [sqlalchemy] #2952: "cascading" declared_attr ?
Brought to you by:
zzzeek
|
From: sqlalchemy <mi...@zz...> - 2014-02-10 22:34:00
|
#2952: "cascading" declared_attr ?
-------------------------+------------------------------------
Reporter: zzzeek | Owner: zzzeek
Type: enhancement | Status: new
Priority: high | Milestone: 0.9.xx
Component: declarative | Severity: major - 1-3 hours
Keywords: | Progress State: not decided upon
-------------------------+------------------------------------
use case:
{{{
#!python
from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base, declared_attr,
cascading_declared_attr
Base = declarative_base()
class HasId(object):
@cascading_declared_attr
def id(cls):
if cls.__name__=='Content':
return Column('id', Integer, primary_key=True)
else:
return Column('id', Integer, ForeignKey('content.id'),
primary_key=True)
class Content(HasId, Base):
content_type = Column(String(20))
@cascading_declared_attr
def __tablename__(cls):
return cls.__name__.lower()
class Project(Content):
currency = Column(String(3))
target = Column(Integer)
current = Column(Integer)
}}}
{{{
#!diff
diff --git a/lib/sqlalchemy/ext/declarative/__init__.py
b/lib/sqlalchemy/ext/declarative/__init__.py
index 0ee4e33..4566324 100644
--- a/lib/sqlalchemy/ext/declarative/__init__.py
+++ b/lib/sqlalchemy/ext/declarative/__init__.py
@@ -1300,7 +1300,7 @@ Mapped instances then make usage of
from .api import declarative_base, synonym_for, comparable_using, \
instrument_declarative, ConcreteBase, AbstractConcreteBase, \
DeclarativeMeta, DeferredReflection, has_inherited_table,\
- declared_attr, as_declarative
+ declared_attr, cascading_declared_attr, as_declarative
__all__ = ['declarative_base', 'synonym_for', 'has_inherited_table',
diff --git a/lib/sqlalchemy/ext/declarative/api.py
b/lib/sqlalchemy/ext/declarative/api.py
index 2418c6e..dc6c42b 100644
--- a/lib/sqlalchemy/ext/declarative/api.py
+++ b/lib/sqlalchemy/ext/declarative/api.py
@@ -162,6 +162,26 @@ class declared_attr(interfaces._MappedAttribute,
property):
def __get__(desc, self, cls):
return desc.fget(cls)
+class cascading_declared_attr(declared_attr):
+ """A :class:`.declared_attr` that will be invoked for all subclasses.
+
+ Use :class:`.cascading_declared_attr` when a particular
``@declared_attr``
+ needs to be invoked individually for each subclass in a hierarchy::
+
+ class HasId(object):
+ @cascading_declared_attr
+ def id(cls):
+ if has_inherited_table(cls):
+ return Column(Integer, ForeignKey("content.id"),
primary_key=True)
+ else:
+ return Column(Integer, primary_key=True)
+
+ class Content(HasId, Base):
+ pass
+
+ class SubContent(Content):
+ pass
+ """
def declarative_base(bind=None, metadata=None, mapper=None, cls=object,
name='Base', constructor=_declarative_constructor,
diff --git a/lib/sqlalchemy/ext/declarative/base.py
b/lib/sqlalchemy/ext/declarative/base.py
index 4fda9c7..a8702cc 100644
--- a/lib/sqlalchemy/ext/declarative/base.py
+++ b/lib/sqlalchemy/ext/declarative/base.py
@@ -31,7 +31,7 @@ def _declared_mapping_info(cls):
def _as_declarative(cls, classname, dict_):
- from .api import declared_attr
+ from .api import declared_attr, cascading_declared_attr
# dict_ will be a dictproxy, which we can't write to, and we need to!
dict_ = dict(dict_)
@@ -96,6 +96,12 @@ def _as_declarative(cls, classname, dict_):
"not applying to subclass %s."
% (base.__name__, name, base, cls))
continue
+ elif isinstance(obj, cascading_declared_attr):
+ ret = obj.__get__(obj, cls)
+ dict_[name] = column_copies[obj] = ret
+ if isinstance(ret, (Column, MapperProperty)) and \
+ ret.doc is None:
+ ret.doc = obj.__doc__
elif base is not cls:
# we're a mixin.
if isinstance(obj, Column):
@@ -125,8 +131,8 @@ def _as_declarative(cls, classname, dict_):
"be declared as @declared_attr callables "
"on declarative mixin classes.")
elif isinstance(obj, declarative_props):
- dict_[name] = ret = \
- column_copies[obj] = getattr(cls, name)
+ ret = getattr(cls, name)
+ dict_[name] = column_copies[obj] = ret
if isinstance(ret, (Column, MapperProperty)) and \
ret.doc is None:
ret.doc = obj.__doc__
}}}
--
Ticket URL: <http://www.sqlalchemy.org/trac/ticket/2952>
sqlalchemy <http://www.sqlalchemy.org/>
The Database Toolkit for Python
|