Этот код не будет работать, потому что polymorphic_identity
класса используется в качестве словарного ключа. По той же причине, по которой [] in {}
поднимает TypeError
, то же самое происходит и с вашим кодом.
Обычный способ сделать что-то подобное - создать класс CEO
, который был бы подклассом Manager
, который мог бы иметь как собственный polymorphic_identity
, так и Mapper
. Но, кроме всего прочего, вы можете создать второй polymorphic_identity
для данного класса (но это довольно хакерски).
Каждый класс, который наследуется от Base
, имеет ссылку на тот же polymorphic_map
, который является просто dict
:
from sqlalchemy.orm import class_mapper
emp_mapper = class_mapper(Employee)
mgr_mapper = class_mapper(Manager)
print(type(emp_mapper.polymorphic_map)) # <class 'dict'>
print(emp_mapper.polymorphic_map is mgr_mapper.polymorphic_map) # True
polymorphic_map
отображает polymorphic_identity
в Mapper
, так что когда данная строка извлекается из базы данных, значение столбца, назначенного столбцу polymorphic_identity
, можно использовать для получения класса, который должен использоваться для представления этих данных. Например, после определения только вашего Employee
класса polymorphic_map
выглядит так: {'employee': <Mapper at 0x1b1eafcac50; Employee>}
. И после определения класса Manager
с 'manager'
как polymorphic_identity
это выглядит так: {'employee': <Mapper at 0x25d10b19cf8; Employee>, 'manager': <Mapper at 0x25d0fdbd4e0; Manager>}
Я создам несколько тестовых данных (мне пришлось удалить все ссылки на таблицу компании - см. MCVE ):
Base.metadata.drop_all(engine)
Base.metadata.create_all(engine)
s = Session()
engineer = Employee(name='engineer')
manager1 = Manager(name='manager1')
manager2 = Manager(name='manager2')
s.add_all([engineer, manager1, manager2])
s.commit()
print(s.query(Employee).all()) # [<__main__.Employee object at 0x000001E5E54A0550>, <__main__.Manager object at 0x000001E5E54A0CF8>, <__main__.Manager object at 0x000001E5E54A0D68>]
print(s.query(Manager).all()) # [<__main__.Manager object at 0x000001E5E54A0CF8>, <__main__.Manager object at 0x000001E5E54A0D68>]
Теперь давайте повысим менеджера до генерального директора:
manager1.type = 'ceo'
s.commit()
Это выдает предупреждение:
SAWarning: Flushing object <Manager at 0x1e5e54a0cf8> with incompatible polymorphic identity 'ceo'; the object may not refresh and/or load correctly (this warning may be suppressed after 10 occurrences)
Но мы проигнорируем это и попытаемся запросить таблицу сотрудников:
print(s.query(Employee).all())
Поднимает:
Traceback (most recent call last):
File "C:\Users\peter_000\.virtualenvs\test-_0Fb_hDQ\lib\site-packages\sqlalchemy\orm\loading.py", line 721, in configure_subclass_mapper
sub_mapper = mapper.polymorphic_map[discriminator]
KeyError: 'ceo'
Таким образом, столбец type
для manager1 теперь имеет значение, которого нет в polymorphic_map
, и мы получаем ключевую ошибку. Поскольку нас интересует только когда генеральный директор представлен как Manager
, мы можем просто вручную вставить запись в polymorphic_map
, связав ключ 'ceo' с Mapper
для класса Manager
. Например:
mgr_mapper.polymorphic_map['ceo'] = mgr_mapper
Теперь давайте снова запросим таблицу сотрудников:
print(s.query(Employee).all()) # [<__main__.Employee object at 0x0000020EE7320550>, <__main__.Manager object at 0x0000020EE7320CF8>, <__main__.Manager object at 0x0000020EE7320D68>]
Обратите внимание, что теперь он снова печатает два объекта менеджера.
Отказ от ответственности: Внутри Mapper
для Manager
он поддерживает ссылку на polymorphic_identity
класса (то есть 'manager'
), и поэтому наша клавиша ceo
в polymorphic_map
указывает на маппер который ссылается на polymorphic_identity
из manager
. Я упоминаю об этом, поскольку в этом примере все работает нормально, я не знаю, может ли это вызвать ошибки в других местах sqlalchemy. Итак, если вы используете что-то подобное в производстве, убедитесь, что оно хорошо протестировано.