On Fri, Sep 22, 2017 at 9:42 AM, Simon King <si...@simonking.org.uk> wrote: > On Fri, Sep 22, 2017 at 8:18 AM, John Smith <johnsmith31...@gmail.com> wrote: >> I have the following database schema: >> >> Table "Parent": >> 1. id - primary key identifier. >> 2. type - polymorphic_identity. >> 3. name - string data column. >> >> Table "Child" - inherits Parent: >> 1. id - primary key identifier. >> 2. parent_id - foreignkey to Parent. >> 3. category - string data column. >> >> Summing up I have two tables. Table Child inherits from Parent and also have >> a foreignkey to it. >> I really need both inheritance and foreignkey. This example is only a short >> demo which reproduces the problem. >> My real database has 1000+ tables with complex inheritance. >> >> I used declarative_base to declare the schema: >> >> >>> # -*- coding: utf-8 -*- >>> >>> from sqlalchemy import Column, String, Integer, ForeignKey >>> from sqlalchemy.orm import relationship >>> from sqlalchemy.ext.declarative import declarative_base >>> from sqlalchemy import create_engine >>> from sqlalchemy.orm import sessionmaker >>> >>> Base = declarative_base() >>> >>> class Parent(Base): >>> __tablename__ = "Parent" >>> id = Column(Integer, primary_key=True) >>> type = Column(String(250)) >>> >>> name = Column(String(250)) >>> >>> __mapper_args__ = { >>> 'polymorphic_identity':'Parent', >>> 'polymorphic_on':type >>> } >>> >>> class Child(Parent): >>> __tablename__ = 'Child' >>> id = Column(Integer, ForeignKey('Parent.id'), primary_key=True) >>> >>> parent_id = Column(ForeignKey("Parent.id"), nullable=True) >>> category = Column(String(250)) >>> >>> __mapper_args__ = { >>> 'polymorphic_identity':'Child', >>> } >>> >>> engine = create_engine('postgresql+psycopg2://joe:joe@localhost/alch') >>> >>> session = sessionmaker() >>> session.configure(bind=engine) >>> Base.metadata.create_all(engine) >> >> >> But when I run the code I get the following error: >> >> >>> sqlalchemy.exc.AmbiguousForeignKeysError: Can't determine join between >>> 'Parent' and 'Child'; tables have more than one foreign key constraint >>> relationship between them. Please specify the 'onclause' of this join >>> explicitly. >> >> >> I have tried to set relationship attribute for Parent or for Child >> separately and for both too. Tried to use primaryjoin and foreign_keys >> parameters of relationship. But the error was the same. >> >> I'm totally confused about this error. >> Please help. Thanks. >> > > The docs at http://docs.sqlalchemy.org/en/latest/orm/inheritance.html say: > > It is most common that the foreign key constraint > is established on the same column or columns > as the primary key itself, however this is not > required; a column distinct from the primary key > may also be made to refer to the parent via foreign > key. The way that a JOIN is constructed from the > base table to subclasses is also directly > customizable, however this is rarely necessary. > > The last sentence is the important one. Normally there is only a > single foreign key from the child to the parent table, so SA can > automatically figure out the join condition. In your case, you've got > 2 foreign keys, so SA is asking you to be explicit. However, it's the > *inheritance* join that is causing the problem, and the main docs > don't describe how to customize that. > > However, if you look at the docs for the underlying mapper() function, > you'll find an "inherit_condition" parameter: > > http://docs.sqlalchemy.org/en/latest/orm/mapping_api.html#sqlalchemy.orm.mapper.params.inherit_condition > > Unfortunately it doesn't seem to accept strings, so you need to > construct your code such that you have direct access to the classes > themselves. > > Here's a working version of your script: > > # -*- coding: utf-8 -*- > > from sqlalchemy import Column, String, Integer, ForeignKey > from sqlalchemy.orm import relationship > from sqlalchemy.ext.declarative import declarative_base > from sqlalchemy import create_engine > from sqlalchemy.orm import sessionmaker > > Base = declarative_base() > > class Parent(Base): > __tablename__ = "Parent" > id = Column(Integer, primary_key=True) > type = Column(String(250)) > > name = Column(String(250)) > > __mapper_args__ = { > 'polymorphic_identity':'Parent', > 'polymorphic_on':type > } > > class Child(Parent): > __tablename__ = 'Child' > id = Column(Integer, ForeignKey('Parent.id'), primary_key=True) > > parent_id = Column(ForeignKey("Parent.id"), nullable=True) > category = Column(String(250)) > > parent = relationship(Parent, foreign_keys=[parent_id]) > > __mapper_args__ = { > 'polymorphic_identity':'Child', > 'inherit_condition': id == Parent.id, > } > > engine = create_engine('sqlite:///:memory:', echo=True) > > Session = sessionmaker() > Session.configure(bind=engine) > Base.metadata.create_all(engine) > > session = Session() > parent = Parent(name='Bob') > child = Child(name='Joe', category='cheese') > session.add_all([parent, child]) > session.flush() > > > Hope that helps, > > Simon
Sorry, I meant for the child object at the end to be constructed like this: child = Child(name='Joe', category='cheese', parent=parent) ...to prove that the relationship to the parent worked. Simon -- SQLAlchemy - The Python SQL Toolkit and Object Relational Mapper http://www.sqlalchemy.org/ To post example code, please provide an MCVE: Minimal, Complete, and Verifiable Example. See http://stackoverflow.com/help/mcve for a full description. --- You received this message because you are subscribed to the Google Groups "sqlalchemy" group. To unsubscribe from this group and stop receiving emails from it, send an email to sqlalchemy+unsubscr...@googlegroups.com. To post to this group, send email to sqlalchemy@googlegroups.com. Visit this group at https://groups.google.com/group/sqlalchemy. For more options, visit https://groups.google.com/d/optout.