After much research and digging, it seems I found the answer, finally. Since I found many pieces of other people who could hardly solve this and could not find a complete and clear answer, I thought that I could publish it here for future travelers.
If you come across this question, you may not really be looking for a tripartite lot for many. I thought it was, but I was not.
Summary: I have users, teams, and roles. If a user joins a team, he is also assigned a role in that team.
I went back to the scratch board and drew what I really wanted:
+---------+---------+---------+ | user_id | team_id | role_id | +---------+---------+---------+ | 1 | 1 | 1 | +---------+---------+---------+
Then it became clear to me that I really did not look for tripartite "many to many", but rather for tripartite "one to many", coming out of the fourth model.
class Membership(db.Model): user_id = db.Column(db.Integer, db.ForeignKey('user.id'), primary_key=True) team_id = db.Column(db.Integer, db.ForeignKey('team.id'), primary_key=True) role_id = db.Column(db.Integer, db.ForeignKey('role.id'), primary_key=True) db.UniqueConstraint('user_id', 'team_id', 'role_id') db.relationship('User', uselist=False, backref='memberships', lazy='dynamic') db.relationship('Team', uselist=False, backref='memberships', lazy='dynamic') db.relationship('Role', uselist=False, backref='memberships', lazy='dynamic') def __init__(self, user, team, role): self.user_id = user.id self.team_id = team.id self.role_id = role.id def __repr__(self): return "<Membership(%s)>"
Case 42: This is exactly the answer I was looking for - I just asked the wrong question.