Of course! Thanks for the quick reply, I was afraid/hoping it would be something simple I was missing -- I wasn't aware that it was required on both sides of a bi-directional relationship, but that makes sense. I'm using 1.3.17 and it does the same, I just only copied the first part of the traceback, assuming it was the root cause. Not to mention I was blind to what is was saying since I thought it was covered by having it on one side. Two lessons learned today!
Thanks again, Brendan On Saturday, August 1, 2020 at 7:15:44 PM UTC-4, Mike Bayer wrote: > > hi and thanks for the straightforward test case. > > I'm not sure if that's an old version of SQLAlchemy you're using, when I > run with current 1.3.18 release the error message is more descriptive: > > "Could not determine join condition between parent/child tables on > relationship ACollection.members - there are multiple foreign key paths > linking the tables. Specify the 'foreign_keys' argument, providing a list > of those columns which should be counted as containing a foreign key > reference to the parent table." > > the error message using a modern version should lead you right to the > issue which is that you forgot to put "foreign_keys" on the "members" > relationship: > > setattr( > collection_class, > "members", > relationship( > member_cls.__name__, > back_populates=f"{cls_name}Rel", > foreign_keys=f"[{member_cls.__name__}.{fk_col}]", > ), > ) > > > > > > On Sat, Aug 1, 2020, at 1:54 PM, Brendan Blanchard wrote: > > I have SQLA classes in a project that have date and data components and > I'm attempting to create a single base-class that I can subclass to create > statistics for all points of data during some interval, but I want the > interval subclasses to maintain references to their sub-data such that if I > change the underlying data of a monthly period, the statistics on that > month can be recalculated (all of this behavior is left out, except the > relationships). In my real use-case, the Member class could be one of > dozens of SQLA classes, each of which could have multiple collection > classes related to it, which is why dynamic creation using type( ) is > necessary. > > In a basic example, I have some data class that needs to be related to an > arbitrary number of other collection classes (created dynamically). One > created class might aggregate those data in 30-minute intervals, and > another in monthly intervals. To do this, my thought was to create one base > class that, when subclassed, is assigned a unique relationship to the > member class. This is what I believe I'm doing, but I am running into > sqlalchemy.exc.AmbiguousForeignKeysError. I've looked around extensively, > and am reaching out here because my understanding is I'm doing what's > required to have multiple relationships between two tables, but am still > getting the error. I'm aware it could be an issue with how I'm creating > subclasses as well, but haven't had any luck using inherit_conditions, > either. The output and trimmed-down example is below: > > Setting ACollection_id on Member with ForeignKey(collections.id) > Setting "members" attribute on ACollection which back populates > "ACollectionRel" > Setting attribute "ACollectionRel" on Member with: > foreign_keys=[Member.ACollection_id] > > Setting BCollection_id on Member with ForeignKey(collections.id) > Setting "members" attribute on BCollection which back populates > "BCollectionRel" > Setting attribute "BCollectionRel" on Member with: > foreign_keys=[Member.BCollection_id] > > > Traceback (most recent call last): > File > "/home/brendan/PycharmProjects/BoulderAIRAnalysis/venv/lib/python3.7/site-packages/sqlalchemy/orm/relationships.py", > > line 2620, in _determine_joins > consider_as_foreign_keys=consider_as_foreign_keys, > File "<string>", line 2, in join_condition > File "<string>", line 2, in _join_condition > File > "/home/brendan/PycharmProjects/BoulderAIRAnalysis/venv/lib/python3.7/site-packages/sqlalchemy/util/deprecations.py", > > line 139, in warned > return fn(*args, **kwargs) > File > "/home/brendan/PycharmProjects/BoulderAIRAnalysis/venv/lib/python3.7/site-packages/sqlalchemy/sql/selectable.py", > > line 967, in _join_condition > a, b, constraints, consider_as_foreign_keys > File > "/home/brendan/PycharmProjects/BoulderAIRAnalysis/venv/lib/python3.7/site-packages/sqlalchemy/sql/selectable.py", > > line 1084, in _joincond_trim_constraints > "join explicitly." % (a.description, b.description) > sqlalchemy.exc.AmbiguousForeignKeysError: Can't determine join between > 'collections' and 'basic_members'; tables have more than one foreign key > constraint relationship between them. Please specify the 'onclause' of this > join explicitly. > > > from sqlalchemy import Column, String, Integer, DateTime, UniqueConstraint, > ForeignKey > from sqlalchemy.orm import relationship > from sqlalchemy.ext.declarative import declarative_base > from sqlalchemy.orm import sessionmaker > from sqlalchemy import create_engine > > Base = declarative_base() > > > class Member(Base): > """A testable basic SQLA class that contains some floating-point > information.""" > __tablename__ = 'basic_members' > > id = Column(Integer, primary_key=True) > > > class CollectionBase(Base): > id = Column(Integer, primary_key=True) > date = Column(DateTime) > type = Column(String(15)) > > __tablename__ = 'collections' > __table_args__ = ( > UniqueConstraint('date', 'type'), > {'extend_existing': True} > ) > __mapper_args__ = { > 'polymorphic_on': 'type', > 'polymorphic_identity': 'base_class', > } > > > def collection_class_factory(name, member_cls): > > all_attrs = { > '__mapper_args__': { > 'polymorphic_identity': name > }, > > 'member_class': member_cls, > *# keep a reference to the member class on the 'collection' class* } > > cls_name = f'{name}Collection' > fk_col = f'{cls_name}_id' > > collection_class = type(cls_name, (CollectionBase,), all_attrs) > > print(f'Setting {fk_col} on {member_cls.__name__} with > ForeignKey({collection_class.__tablename__}.id)') > > *# set the foreign key col on the members class to reference the periods they > belong to* setattr(member_cls, fk_col, Column(Integer, > ForeignKey(f'{collection_class.__tablename__}.id'))) > > print(f'Setting "members" attribute on {collection_class.__name__} which > back populates "{cls_name}Rel"') > > *# set the relationship from periods -> members* setattr(collection_class, > 'members', relationship(member_cls.__name__, back_populates=f'{cls_name}Rel')) > > print(f'Setting attribute "{cls_name}Rel" on {member_cls.__name__} with: > ') > print(f'\tforeign_keys=[{member_cls.__name__}.{fk_col}]') > > *# set the relationship from members -> periods* setattr(member_cls, > f'{cls_name}Rel', relationship( > collection_class.__name__, uselist=False, > foreign_keys=f'[{member_cls.__name__}.{fk_col}]', > back_populates='members' > )) > print() > > return collection_class > > > CollectionA = collection_class_factory('A', Member) > CollectionB = collection_class_factory('B', Member) > > engine = create_engine('sqlite://') > session = sessionmaker(bind=engine)() > > Base.metadata.create_all(bind=engine) > > a = CollectionA() > b = CollectionB() > > > -- > 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 sqlal...@googlegroups.com <javascript:>. > To view this discussion on the web visit > https://groups.google.com/d/msgid/sqlalchemy/a2dc5325-aa51-45f7-8d8d-388241bb84ceo%40googlegroups.com > > <https://groups.google.com/d/msgid/sqlalchemy/a2dc5325-aa51-45f7-8d8d-388241bb84ceo%40googlegroups.com?utm_medium=email&utm_source=footer> > . > > > -- 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 view this discussion on the web visit https://groups.google.com/d/msgid/sqlalchemy/ec69633b-92f5-4d97-b144-1409f0102422o%40googlegroups.com.