SQLAlchemy relationship on many-to-many association table

2024/9/27 5:46:08

I am trying to build a relationship to another many-to-many relationship, the code looks like this:

from sqlalchemy import Column, Integer, ForeignKey, Table, ForeignKeyConstraint, create_engine
from sqlalchemy.orm import relationship, backref, scoped_session, sessionmaker
from sqlalchemy.ext.declarative import declarative_baseBase = declarative_base()supervision_association_table = Table('supervision', Base.metadata,Column('supervisor_id', Integer, ForeignKey('supervisor.id'), primary_key=True),Column('client_id', Integer, ForeignKey('client.id'), primary_key=True)
)class User(Base):__tablename__ = 'user'id = Column(Integer, primary_key=True)class Supervisor(User):__tablename__ = 'supervisor'__mapper_args__ = {'polymorphic_identity': 'supervisor'}id = Column(Integer, ForeignKey('user.id'), primary_key = True)schedules = relationship("Schedule", backref='supervisor')class Client(User):__tablename__ = 'client'__mapper_args__ = {'polymorphic_identity': 'client'}id = Column(Integer, ForeignKey('user.id'), primary_key = True)supervisor = relationship("Supervisor", secondary=supervision_association_table,backref='clients')schedules = relationship("Schedule", backref="client")class Schedule(Base):__tablename__ = 'schedule'__table_args__ = (ForeignKeyConstraint(['client_id', 'supervisor_id'], ['supervision.client_id', 'supervision.supervisor_id']),)id = Column(Integer, primary_key=True)client_id = Column(Integer, nullable=False)supervisor_id = Column(Integer, nullable=False)engine = create_engine('sqlite:///temp.db')
db_session = scoped_session(sessionmaker(bind=engine))
Base.metadata.create_all(bind=engine)

What I want to do is to relate a schedule to a specific Client-Supervisor-relationship, though I have not found out how to do it. Going through the SQLAlchemy documentation I found a few hints, resulting in the ForeignKeyConstraint on the Schedule-Table.

How can I specify the relationship to have this association work?

Answer

You need to map supervision_association_table so that you can create relationships to/from it.

I may be glossing over something here, but it seems like since you have many-to-many here you really can't have Client.schedules - if I say Client.schedules.append(some_schedule), which row in "supervision" is it pointing to?

The example below provides a read-only "rollup" accessor for those which joins the Schedule collections of each SupervisorAssociation. The association_proxy extension is used to conceal, when convenient, the details of the SupervisionAssociation object.

from sqlalchemy import Column, Integer, ForeignKey, Table, ForeignKeyConstraint, create_engine
from sqlalchemy.orm import relationship, backref, scoped_session, sessionmaker
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.associationproxy import association_proxy
from itertools import chainBase = declarative_base()class SupervisionAssociation(Base):__tablename__ = 'supervision'supervisor_id = Column(Integer, ForeignKey('supervisor.id'), primary_key=True)client_id = Column(Integer, ForeignKey('client.id'), primary_key=True)supervisor = relationship("Supervisor", backref="client_associations")client = relationship("Client", backref="supervisor_associations")schedules = relationship("Schedule")class User(Base):__tablename__ = 'user'id = Column(Integer, primary_key=True)class Supervisor(User):__tablename__ = 'supervisor'__mapper_args__ = {'polymorphic_identity': 'supervisor'}id = Column(Integer, ForeignKey('user.id'), primary_key = True)clients = association_proxy("client_associations", "client", creator=lambda c: SupervisionAssociation(client=c))@propertydef schedules(self):return list(chain(*[c.schedules for c in self.client_associations]))class Client(User):__tablename__ = 'client'__mapper_args__ = {'polymorphic_identity': 'client'}id = Column(Integer, ForeignKey('user.id'), primary_key = True)supervisors = association_proxy("supervisor_associations", "supervisor", creator=lambda s: SupervisionAssociation(supervisor=s))@propertydef schedules(self):return list(chain(*[s.schedules for s in self.supervisor_associations]))class Schedule(Base):__tablename__ = 'schedule'__table_args__ = (ForeignKeyConstraint(['client_id', 'supervisor_id'], ['supervision.client_id', 'supervision.supervisor_id']),)id = Column(Integer, primary_key=True)client_id = Column(Integer, nullable=False)supervisor_id = Column(Integer, nullable=False)client = association_proxy("supervisor_association", "client")engine = create_engine('sqlite:///temp.db', echo=True)
db_session = scoped_session(sessionmaker(bind=engine))
Base.metadata.create_all(bind=engine)c1, c2 = Client(), Client()
sp1, sp2 = Supervisor(), Supervisor()
sch1, sch2, sch3 = Schedule(), Schedule(), Schedule()sp1.clients = [c1]
c2.supervisors = [sp2]
c2.supervisor_associations[0].schedules = [sch1, sch2]
c1.supervisor_associations[0].schedules = [sch3]db_session.add_all([c1, c2, sp1, sp2, ])
db_session.commit()print c1.schedules
print sp2.schedules
https://en.xdnf.cn/q/71489.html

Related Q&A

Python: interpolating in a triangular mesh

Is there any decent Pythonic way to interpolate in a triangular mesh, or would I need to implement that myself? That is to say, given a (X,Y) point well call P, and a mesh (vertices at (X,Y) with val…

Customizing pytest junitxml failure reports

I am trying to introspect test failures and include additional data into the junit xml test report. Specifically, this is a suite of functional tests on an external product, and I want to include the p…

python nltk keyword extraction from sentence

"First thing we do, lets kill all the lawyers." - William ShakespeareGiven the quote above, I would like to pull out "kill" and "lawyers" as the two prominent keywords to …

Getting the parameter names of scipy.stats distributions

I am writing a script to find the best-fitting distribution over a dataset using scipy.stats. I first have a list of distribution names, over which I iterate:dists = [alpha, anglit, arcsine, beta, bet…

Does Python 3 gzip closes the fileobj?

The gzip docs for Python 3 states thatCalling a GzipFile object’s close() method does not close fileobj, since you might wish to append more material after the compressed dataDoes this mean that the g…

pip stopped working after upgrading anaconda v4.4 to v5.0

I ran the command conda update anaconda to update anaconda v4.4 to v5.0After anaconda was successfully upgraded to v5.0, I had problems running pip.This is the error output I see after running pip;Trac…

Python Django- How do I read a file from an input file tag?

I dont want the file to be saved on my server, I just want the file to be read and printed out in the next page. Right now I have this.(index.html)<form name="fileUpload" method="post…

ImportError: cannot import name AutoModelWithLMHead from transformers

This is literally all the code that I am trying to run: from transformers import AutoModelWithLMHead, AutoTokenizer import torchtokenizer = AutoTokenizer.from_pretrained("microsoft/DialoGPT-small&…

UnicodeEncodeError: ascii codec cant encode characters in position 0-6: ordinal not in range(128)

Ιve tried all the solution that I could find, but nothing seems to work: teext = str(self.tableWidget.item(row, col).text())Im writing in greek by the way...

selenium PhantomJS send_keys doesnt work

I am using selenium and PhantomJS for testing. I followed Seleniums simple usage, but send_keys doesnt work on PhantomJS, it works on Firefox. Why? I have to use button.click() instead?#!/usr/bin/pyt…