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.

Reply via email to