I have a model classes defined like this. The idea is that that Person
will hold general information about people as well as reference to it's "sub-classes": Woman and Men as parents. Woman and Men will hold information specific for that gender.
I'm using Alembic to generate the migration but I'm getting an error:
sqlalchemy.exc.AmbiguousForeignKeysError: Can't determine join between 'person' and 'woman'; tables have more than one foreign key constraint relationship between them. Please specify the 'onclause' of this join explicitly.
Note that Woman inherits from Person and therefore there is also the reference from woman to person.
Nothing I tried works:
- Remove line
mother = relationship('Woman')
with the idea that SQL alchemy doesn't understand that relationship - Adding
mother_id = Column(Integer, ForeignKey('woman.id'), nullable=True)
- Adding
mother = relationship('Woman', primaryjoin='Person.mother_id==Mother.id')
Specifying onclause isn't possible (or I don't know how) since the join is auto-generated
class Person(Base): __tablename__ = 'person' id = Column(Integer, primary_key=True) gender = Column(String(5)) name = Column(String(32), nullable=False) surname = Column(String(32), nullable=False) age = Column(Float, primary_key=True) mother_id = Column(Integer, ForeignKey('woman.id'), nullable=True) mother = relationship('Woman') _mapper_args__ = { 'polymorphic_on': gender } class Woman(Person): __tablename__ = 'woman' __mapper_args__ = { 'polymorphic_identity': 'woman' } id = Column(Integer, ForeignKey('person.id'), primary_key=True)
Full Trace:
$ alembic revision --autogenerate -m "mother"
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.4/bin/alembic", line 11, in <module>
sys.exit(main())
File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages/alembic/config.py", line 479, in main
CommandLine(prog=prog).main(argv=argv)
File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages/alembic/config.py", line 473, in main
self.run_cmd(cfg, options)
File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages/alembic/config.py", line 456, in run_cmd
**dict((k, getattr(options, k)) for k in kwarg)
File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages/alembic/command.py", line 117, in revision
script_directory.run_env()
File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages/alembic/script/base.py", line 416, in run_env
util.load_python_file(self.dir, 'env.py')
File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages/alembic/util/pyfiles.py", line 93, in load_python_file
module = load_module_py(module_id, path)
File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages/alembic/util/compat.py", line 68, in load_module_py
module_id, path).load_module(module_id)
File "<frozen importlib._bootstrap>", line 539, in _check_name_wrapper
File "<frozen importlib._bootstrap>", line 1614, in load_module
File "<frozen importlib._bootstrap>", line 596, in _load_module_shim
File "<frozen importlib._bootstrap>", line 1220, in load
File "<frozen importlib._bootstrap>", line 1200, in _load_unlocked
File "<frozen importlib._bootstrap>", line 1129, in _exec
File "<frozen importlib._bootstrap>", line 1471, in exec_module
File "<frozen importlib._bootstrap>", line 321, in _call_with_frames_removed
File "migrations/env.py", line 24, in <module>
import api.model
File "/Users/jakubt/Projects/hex/api/model.py", line 31, in <module>
class Woman(Person):
File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages/sqlalchemy/ext/declarative/api.py", line 64, in __init__
_as_declarative(cls, classname, cls.__dict__)
File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages/sqlalchemy/ext/declarative/base.py", line 88, in _as_declarative
_MapperConfig.setup_mapping(cls, classname, dict_)
File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages/sqlalchemy/ext/declarative/base.py", line 103, in setup_mapping
cfg_cls(cls_, classname, dict_)
File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages/sqlalchemy/ext/declarative/base.py", line 135, in __init__
self._early_mapping()
File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages/sqlalchemy/ext/declarative/base.py", line 138, in _early_mapping
self.map()
File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages/sqlalchemy/ext/declarative/base.py", line 530, in map
**self.mapper_args
File "<string>", line 2, in mapper
File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages/sqlalchemy/orm/mapper.py", line 671, in __init__
self._configure_inheritance()
File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages/sqlalchemy/orm/mapper.py", line 978, in _configure_inheritance
self.local_table)
File "<string>", line 2, in join_condition
File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages/sqlalchemy/sql/selectable.py", line 965, in _join_condition
a, b, constraints, consider_as_foreign_keys)
File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages/sqlalchemy/sql/selectable.py", line 1055, in _joincond_trim_constraints
"join explicitly." % (a.description, b.description))
sqlalchemy.exc.AmbiguousForeignKeysError: Can't determine join between 'person' and 'woman'; tables have more than one foreign key constraint relationship between them. Please specify the 'onclause' of this join explicitly.
What I see as issue here is that you are referencing foreign key to foreign key.
And you create circular dependancy. You cannot create that dependency in any single creation statement in any SQL dialect. You'll first need to create
person
table, thenwoman
table, then addforeign key (mother_id)
from first to second table (note that you cannot create women table first as it has foreign key to person table).I don't think alembic is smart enough to do this, and I think that what you want to achieve is not good thing to do.
Here is what I suggest:
woman.id
will always have same value for one record asperson.id
, so first foreign key should reference to person table instead of mother table.Change definition of
mother_id
andmother
:Tip: you don't need second table if you're storing just id (just remove
__tablename__
from second class)And check in code for gender when adding mother/father.