How to design 2 different many-to-many relationships between same 2 tables with backref's / back_populate's

StackOverflow https://stackoverflow.com/questions/23636220

  •  21-07-2023
  •  | 
  •  

Вопрос

This is the situation I am trying to model:

There are users, which can be classified as rider or driver. Each ride has a rider and a driver. I can imagine working with a ride object, where I can call ride.driver or ride.rider. But I find it difficult to reason about it on the other direction. For any user u, when I call u.rides, it is not clear if it should return rides where u is the driver or the rider.

I can also seperately have two relationships, so that u.rides_as_driver returns rides where u is a driver and u.rides_as_rider to return rides where u is a rider.

Is this a good approach to model the relationship that I just described?

Update

If this is a good approach, how do I acheive this in SQLAlchemy? In particular, how do I use backref in this design?

What follows is my attempt:

class Ride:
    driver = db.Relationship('User', primaryjoin=driver_id==user.id)
    rider =  db.Relationship('User', primaryjoin=rider_id==user.id)
class User:
    rides_as_rider = db.Relationship('Ride', primaryjoin=id==ride.rider_id)
    rides_as_driver = db.Relationship('Ride', primaryjoin=id==ride.driver_id)
Это было полезно?

Решение

Indeed, your conclusion that you should use two relationships reflects that this is not only a good way to represent this connection, it is the only way

relationship models not the remote table, Ride in your case, but the connections between tables. There are two connections between Ride and User, for the two roles each user may play in a ride.

For more evidence of this fact, notice that you also have two relationship's facing away from Ride, toward User, one for each branch of the connection. Since you have two of those, you would also require distinct backref's, one for each.

But, your code is not quite enough. You should tell sqlalchemy that "this relationship is that one, only backwards". as it is, you have four relationships, none of whom know the existence of the other. This will manifest as mysterious effect of changes in Ride.driver not reflected in User.rides_as_driver.

The fix is easy, though, add a back_populates to each relationship and sqlalchemy will keep them in sync:

class Ride:
    driver = db.Relationship('User', primaryjoin=driver_id==user.id, back_populates="rides_as_driver")
    rider =  db.Relationship('User', primaryjoin=rider_id==user.id, back_populates="rides_as_rider")
class User:
    rides_as_rider = db.Relationship('Ride', primaryjoin=id==ride.rider_id, back_populates="rider")
    rides_as_driver = db.Relationship('Ride', primaryjoin=id==ride.driver_id, back_populates="driver")
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top