[Sqlalchemy-tickets] [sqlalchemy] #2865: declarative reflective base needs to handle "secondary"
Brought to you by:
zzzeek
|
From: sqlalchemy <mi...@zz...> - 2013-11-16 22:21:08
|
#2865: declarative reflective base needs to handle "secondary"
-------------------------+---------------------------------------
Reporter: zzzeek | Owner: zzzeek
Type: defect | Status: new
Priority: high | Milestone: 0.9.0
Component: declarative | Severity: minor - half an hour
Keywords: | Progress State: needs tests
-------------------------+---------------------------------------
{{{
#!python
from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base,
DeferredReflection
Base = declarative_base(cls=DeferredReflection)
class A(Base):
__tablename__ = 'a'
bs = relationship("B", secondary="atob")
class B(Base):
__tablename__ = 'b'
e = create_engine("sqlite://", echo=True)
e.execute("""
create table a(id integer primary key)
""")
e.execute("""
create table b(id integer primary key)
""")
e.execute("""
create table atob(
a_id integer references a(id),
b_id integer references b(id),
primary key (a_id, b_id)
)
""")
Base.prepare(e)
# if this isn't done, failure
#Table('atob', Base.metadata, autoload=True, autoload_with=e)
print A.bs.__clause_element__()
}}}
patch:
{{{
#!diff
diff --git a/lib/sqlalchemy/ext/declarative/api.py
b/lib/sqlalchemy/ext/declarative/api.py
index b309a78..5a8009f 100644
--- a/lib/sqlalchemy/ext/declarative/api.py
+++ b/lib/sqlalchemy/ext/declarative/api.py
@@ -9,9 +9,10 @@
from ...schema import Table, MetaData
from ...orm import synonym as _orm_synonym, mapper,\
comparable_property,\
- interfaces
+ interfaces, properties
from ...orm.util import polymorphic_union
from ...orm.base import _mapper_or_none
+from ...util import compat
from ... import exc
import weakref
@@ -470,6 +471,18 @@ class DeferredReflection(object):
for thingy in to_map:
cls._sa_decl_prepare(thingy.local_table, engine)
thingy.map()
+ for rel in thingy.cls.__mapper__._props.values():
+ if isinstance(rel, properties.RelationshipProperty) and \
+ rel.secondary is not None:
+ if isinstance(rel.secondary, Table):
+ cls._sa_decl_prepare(rel.secondary, engine)
+ elif callable(rel.secondary) and \
+ hasattr(rel.secondary, '_declarative_arg'):
+ cls._sa_decl_prepare(
+ Table(
+ rel.secondary._declarative_arg,
+ cls.metadata,
+ ), engine)
@classmethod
def _sa_decl_prepare(cls, local_table, engine):
diff --git a/lib/sqlalchemy/ext/declarative/clsregistry.py
b/lib/sqlalchemy/ext/declarative/clsregistry.py
index a669e37..f11c7a1 100644
--- a/lib/sqlalchemy/ext/declarative/clsregistry.py
+++ b/lib/sqlalchemy/ext/declarative/clsregistry.py
@@ -225,47 +225,56 @@ def _determine_container(key, value):
return _GetColumns(value)
-def _resolver(cls, prop):
- def resolve_arg(arg):
- import sqlalchemy
- from sqlalchemy.orm import foreign, remote
-
- fallback = sqlalchemy.__dict__.copy()
- fallback.update({'foreign': foreign, 'remote': remote})
-
- def access_cls(key):
- if key in cls._decl_class_registry:
- return _determine_container(key,
cls._decl_class_registry[key])
- elif key in cls.metadata.tables:
- return cls.metadata.tables[key]
- elif key in cls.metadata._schemas:
- return _GetTable(key, cls.metadata)
- elif '_sa_module_registry' in cls._decl_class_registry and \
- key in cls._decl_class_registry['_sa_module_registry']:
- registry =
cls._decl_class_registry['_sa_module_registry']
- return registry.resolve_attr(key)
+class _class_resolver(object):
+ def __init__(self, cls, prop, fallback, arg):
+ self.cls = cls
+ self.prop = prop
+ self.arg = self._declarative_arg = arg
+ self.fallback = fallback
+ self._dict = util.PopulateDict(self._access_cls)
+
+ def _access_cls(self, key):
+ cls = self.cls
+ if key in cls._decl_class_registry:
+ return _determine_container(key,
cls._decl_class_registry[key])
+ elif key in cls.metadata.tables:
+ return cls.metadata.tables[key]
+ elif key in cls.metadata._schemas:
+ return _GetTable(key, cls.metadata)
+ elif '_sa_module_registry' in cls._decl_class_registry and \
+ key in cls._decl_class_registry['_sa_module_registry']:
+ registry = cls._decl_class_registry['_sa_module_registry']
+ return registry.resolve_attr(key)
+ else:
+ return self.fallback[key]
+
+ def __call__(self):
+ try:
+ x = eval(self.arg, globals(), self._dict)
+
+ if isinstance(x, _GetColumns):
+ return x.cls
else:
- return fallback[key]
+ return x
+ except NameError as n:
+ raise exc.InvalidRequestError(
+ "When initializing mapper %s, expression %r failed to "
+ "locate a name (%r). If this is a class name, consider "
+ "adding this relationship() to the %r class after "
+ "both dependent classes have been defined." %
+ (self.prop.parent, self.arg, n.args[0], self.cls)
+ )
- d = util.PopulateDict(access_cls)
- def return_cls():
- try:
- x = eval(arg, globals(), d)
+def _resolver(cls, prop):
+ import sqlalchemy
+ from sqlalchemy.orm import foreign, remote
- if isinstance(x, _GetColumns):
- return x.cls
- else:
- return x
- except NameError as n:
- raise exc.InvalidRequestError(
- "When initializing mapper %s, expression %r failed to
"
- "locate a name (%r). If this is a class name,
consider "
- "adding this relationship() to the %r class after "
- "both dependent classes have been defined." %
- (prop.parent, arg, n.args[0], cls)
- )
- return return_cls
+ fallback = sqlalchemy.__dict__.copy()
+ fallback.update({'foreign': foreign, 'remote': remote})
+
+ def resolve_arg(arg):
+ return _class_resolver(cls, prop, fallback, arg)
return resolve_arg
}}}
--
Ticket URL: <http://www.sqlalchemy.org/trac/ticket/2865>
sqlalchemy <http://www.sqlalchemy.org/>
The Database Toolkit for Python
|