- changed status to wontfix
Using relationship with primaryjoin in declared_attr fails
Issue #2876
resolved
(Almost) minimal test case:
import sqlalchemy
from sqlalchemy import schema
from sqlalchemy import types
from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.declarative import declared_attr
metadata = schema.MetaData()
BaseObject = declarative_base(metadata=metadata)
class Image(BaseObject):
__tablename__ = 'image'
id = schema.Column(types.Integer(), primary_key=True, autoincrement=True)
class AvatarMixin(object):
@declared_attr
def avatar_original_id(cls):
return schema.Column('avatar_original_id', types.Integer(),
schema.ForeignKey('image.id', onupdate='CASCADE', ondelete='RESTRICT'),
unique=True)
@declared_attr
def avatar_original(cls):
return relationship(Image, cascade='all',
primaryjoin=(cls.avatar_original_id == Image.id))
@declared_attr
def avatar_id(cls):
return schema.Column('avatar_id', types.Integer(),
schema.ForeignKey('image.id', onupdate='CASCADE', ondelete='RESTRICT'),
unique=True)
class User(BaseObject, AvatarMixin):
__tablename__ = 'user'
id = schema.Column(types.Integer(), primary_key=True, autoincrement=True)
engine = sqlalchemy.create_engine('sqlite:///:memory:')
metadata.create_all(engine)
User()
results in this output:
...
File "/Users/wichert/Library/buildout/eggs/SQLAlchemy-0.8.3-py2.7-macosx-10.9-x86_64.egg/sqlalchemy/schema.py", line 1026, in references
if fk.column.proxy_set.intersection(column.proxy_set):
File "/Users/wichert/Library/buildout/eggs/SQLAlchemy-0.8.3-py2.7-macosx-10.9-x86_64.egg/sqlalchemy/util/langhelpers.py", line 612, in __get__
obj.__dict__[self.__name__](self.__name__) = result = self.fget(obj)
File "/Users/wichert/Library/buildout/eggs/SQLAlchemy-0.8.3-py2.7-macosx-10.9-x86_64.egg/sqlalchemy/schema.py", line 1456, in column
if schema is None and parenttable.metadata.schema is not None:
AttributeError: 'NoneType' object has no attribute 'metadata'
Comments (2)
-
repo owner -
repo owner docs are updated in 33e77c3077a15c51f30ac5aae724c.
- Log in to comment
TL;DR; - this situation is documented at http://docs.sqlalchemy.org/en/rel_0_9/orm/extensions/declarative.html#mixing-in-relationships
The error message here is improved in 0.9, where you will get:
Your method avatar_original_id creates a new
Column
object each time it is called. When you call it within youravatar_original
method, you get aColumn
back, but that Column object is unknown to any other aspect of the program.So the class gets mapped normally, declarative calls upon
avatar_original_id
to get at theColumn
that will actually be mapped, and the one you've put in yourrelationship()
has no parent table.The solution - except in the most simplistic cases, strings or lambdas should always be used for relationship arguments in declarative:
or
the latter is documented in the last example at: http://docs.sqlalchemy.org/en/rel_0_9/orm/extensions/declarative.html#mixing-in-relationships
the "lambda:" form should probably be added to the docs as it is handy.