uralbash / sqlalchemy_mptt

SQLAlchemy nested sets mixin (MPTT)
http://sqlalchemy-mptt.readthedocs.io
MIT License
196 stars 32 forks source link

polymorphic identity not work #47

Closed uralbash closed 9 years ago

uralbash commented 9 years ago
from sqlalchemy import (
    Column,
    String,
    Integer,
    ForeignKey,
    create_engine
)
from sqlalchemy.orm import create_session
from sqlalchemy.ext.declarative import declarative_base

e = create_engine('sqlite:////tmp/foo.db', echo=True)
Base = declarative_base(bind=e)

from sqlalchemy_mptt import BaseNestedSets

class Employee(Base, BaseNestedSets):
    __tablename__ = 'employees'

    id = Column(Integer, primary_key=True)
    name = Column(String(50))
    type = Column(String(30), nullable=False)

    __mapper_args__ = {'polymorphic_on': type}

    def __init__(self, name):
        self.name = name

class Manager(Employee):
    __tablename__ = 'managers'
    __mapper_args__ = {'polymorphic_identity': 'manager'}

    employee_id = Column(Integer, ForeignKey('employees.id'),
                         primary_key=True)
    manager_data = Column(String(50))

    def __init__(self, name, manager_data):
        super(Manager, self).__init__(name)
        self.manager_data = manager_data

class Owner(Manager):
    __tablename__ = 'owners'
    __mapper_args__ = {'polymorphic_identity': 'owner'}

    employee_id = Column(Integer, ForeignKey('managers.employee_id'),
                         primary_key=True)
    owner_secret = Column(String(50))

    def __init__(self, name, manager_data, owner_secret):
        super(Owner, self).__init__(name, manager_data)
        self.owner_secret = owner_secret

Base.metadata.drop_all()
Base.metadata.create_all()

s = create_session(bind=e, autoflush=True, autocommit=False)
o = Owner('nosklo', 'mgr001', 'ownerpwd')
s.add(o)
s.commit()

raises error

Traceback (most recent call last):
  File "polymorphic.py", line 30, in <module>
    class Manager(Employee):
  File "/home/uralbash/.virtualenvs/sacrud/lib/python3.4/site-packages/SQLAlchemy-1.0.6-py3.4-linux-x86_64.egg/sqlalchemy/ext/declarative/api.py", line 55, in __init__
    _as_declarative(cls, classname, cls.__dict__)
  File "/home/uralbash/.virtualenvs/sacrud/lib/python3.4/site-packages/SQLAlchemy-1.0.6-py3.4-linux-x86_64.egg/sqlalchemy/ext/declarative/base.py", line 88, in _as_declarative
    _MapperConfig.setup_mapping(cls, classname, dict_)
  File "/home/uralbash/.virtualenvs/sacrud/lib/python3.4/site-packages/SQLAlchemy-1.0.6-py3.4-linux-x86_64.egg/sqlalchemy/ext/declarative/base.py", line 103, in setup_mapping
    cfg_cls(cls_, classname, dict_)
  File "/home/uralbash/.virtualenvs/sacrud/lib/python3.4/site-packages/SQLAlchemy-1.0.6-py3.4-linux-x86_64.egg/sqlalchemy/ext/declarative/base.py", line 131, in __init__
    self._setup_table()
  File "/home/uralbash/.virtualenvs/sacrud/lib/python3.4/site-packages/SQLAlchemy-1.0.6-py3.4-linux-x86_64.egg/sqlalchemy/ext/declarative/base.py", line 394, in _setup_table
    **table_kw)
  File "/home/uralbash/.virtualenvs/sacrud/lib/python3.4/site-packages/SQLAlchemy-1.0.6-py3.4-linux-x86_64.egg/sqlalchemy/sql/schema.py", line 416, in __new__
    metadata._remove_table(name, schema)
  File "/home/uralbash/.virtualenvs/sacrud/lib/python3.4/site-packages/SQLAlchemy-1.0.6-py3.4-linux-x86_64.egg/sqlalchemy/util/langhelpers.py", line 60, in __exit__
    compat.reraise(exc_type, exc_value, exc_tb)
  File "/home/uralbash/.virtualenvs/sacrud/lib/python3.4/site-packages/SQLAlchemy-1.0.6-py3.4-linux-x86_64.egg/sqlalchemy/util/compat.py", line 182, in reraise
    raise value
  File "/home/uralbash/.virtualenvs/sacrud/lib/python3.4/site-packages/SQLAlchemy-1.0.6-py3.4-linux-x86_64.egg/sqlalchemy/sql/schema.py", line 411, in __new__
    table._init(name, metadata, *args, **kw)
  File "/home/uralbash/.virtualenvs/sacrud/lib/python3.4/site-packages/SQLAlchemy-1.0.6-py3.4-linux-x86_64.egg/sqlalchemy/sql/schema.py", line 488, in _init
    self._init_items(*args)
  File "/home/uralbash/.virtualenvs/sacrud/lib/python3.4/site-packages/SQLAlchemy-1.0.6-py3.4-linux-x86_64.egg/sqlalchemy/sql/schema.py", line 72, in _init_items
    item._set_parent_with_dispatch(self)
  File "/home/uralbash/.virtualenvs/sacrud/lib/python3.4/site-packages/SQLAlchemy-1.0.6-py3.4-linux-x86_64.egg/sqlalchemy/sql/base.py", line 433, in _set_parent_with_dispatch
    self._set_parent(parent)
  File "/home/uralbash/.virtualenvs/sacrud/lib/python3.4/site-packages/SQLAlchemy-1.0.6-py3.4-linux-x86_64.egg/sqlalchemy/sql/schema.py", line 3122, in _set_parent
    ColumnCollectionMixin._set_parent(self, table)
  File "/home/uralbash/.virtualenvs/sacrud/lib/python3.4/site-packages/SQLAlchemy-1.0.6-py3.4-linux-x86_64.egg/sqlalchemy/sql/schema.py", line 2459, in _set_parent
    col = table.c[col]
  File "/home/uralbash/.virtualenvs/sacrud/lib/python3.4/site-packages/SQLAlchemy-1.0.6-py3.4-linux-x86_64.egg/sqlalchemy/util/_collections.py", line 193, in __getitem__
    return self._data[key]
KeyError: 'lft'
uralbash commented 9 years ago

Solved by adding an attribute __table_args__ = tuple() in each child table.

class Manager(Employee):
    __tablename__ = 'managers'
    __mapper_args__ = {'polymorphic_identity': 'manager'}
    __table_args__ = tuple()

    employee_id = Column(Integer, ForeignKey('employees.id'),
                         primary_key=True)
    manager_data = Column(String(50))

    def __init__(self, name, manager_data):
        super(Manager, self).__init__(name)
        self.manager_data = manager_data

class Owner(Manager):
    __tablename__ = 'owners'
    __mapper_args__ = {'polymorphic_identity': 'owner'}
    __table_args__ = tuple()

    employee_id = Column(Integer, ForeignKey('managers.employee_id'),
                         primary_key=True)
    owner_secret = Column(String(50))

    def __init__(self, name, manager_data, owner_secret):
        super(Owner, self).__init__(name, manager_data)
        self.owner_secret = owner_secret