多对多,三张表相互关联(SqlAlchemy)

Anu*_*raz 5 python many-to-many sqlalchemy flask-sqlalchemy

我有三个表用户、设备和角色。我像这样创建了一个多对多关系 b/w User 和 Device;

#Many-to-Many relation between User and Devices
userDevices = db.Table("user_devices",
                       db.Column("id", db.Integer, primary_key=True),
                       db.Column("user_id", db.Integer, db.ForeignKey("user.id")),
                       db.Column("device_id", db.Integer, db.ForeignKey("device.id"))))

class User(db.Model):
    __tablename__ = 'user'
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(60), index=True, unique=True)
    devices = db.relationship("Device", secondary=userDevices, backref=db.backref('users'), lazy="dynamic")

class Device(db.Model):
    __tablename__ = 'device'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(60), unique=True)
Run Code Online (Sandbox Code Playgroud)

这很安静。我可以将设备分配d1给用户u1> d1.users.append(u1),将用户分配给设备 >u1.devices.append(d1)db.session.commit()

我想要更多的是user_devices用一列扩展表,作为role_id角色表的外键。以便此表user_devices将清楚地描述一个Role特定User于特定的Device. role_id在表中添加一列后,user_devices我将Role表描述为;

class Role(db.Model):
    __tablename__ = 'role'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(60), unique=True)
    device = db.relationship("Device", secondary=userDevices, backref=db.backref('roles'), lazy="dynamic")
Run Code Online (Sandbox Code Playgroud)

这样,我如何r1u1设备上的用户分配角色d1?这是我尝试过的:

# First get the device, user and role 
deviceRow = db.session.query(Device).filter(Device.name=="d1").first()
userRow = db.session.query(User).filter(User.username=="u1").first()
roleRow = db.session.query(Role).filter(Role.name == "r1").first()
# Then add the user on that device
deviceRow.users.append(userRow)
deviceRow.roles.append(roleRow)
Run Code Online (Sandbox Code Playgroud)

这将在表中创建两行 user_devices

在此处输入图片说明

有什么办法可以像这样将两个属性添加到表中吗?

deviceRow.users.append(userRow).roles.append(roleRow)
Run Code Online (Sandbox Code Playgroud)

以便在 commit() 之后只创建一行?

Ilj*_*ilä 7

3 个实体的关联不再是简单的多对多关系。您需要的是关联对象模式。为了更容易处理关联,将其映射为模型类而不是简单的Table

class UserDevice(db.Model):
    __tablename__ = "user_devices"

    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.Integer, db.ForeignKey("user.id"), nullable=False)
    device_id = db.Column(db.Integer, db.ForeignKey("device.id"), nullable=False)
    role_id = db.Column(db.Integer, db.ForeignKey("role.id"), nullable=False)

    __table_args__ = (db.UniqueConstraint(user_id, device_id, role_id),)

    user = db.relationship("User", back_populates="user_devices")
    device = db.relationship("Device")
    role = db.relationship("Role", back_populates="user_devices")

class User(db.Model):
    __tablename__ = "user"
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(60), index=True, unique=True)
    user_devices = db.relationship("UserDevice", back_populates="user")

class Role(db.Model):
    __tablename__ = "role"

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(60), unique=True)
    user_devices = db.relationship("UserDevice", back_populates="role")
Run Code Online (Sandbox Code Playgroud)

要将用户与设备和角色相关联,请创建一个新UserDevice对象:

device = db.session.query(Device).filter(Device.name == "d1").first()
user = db.session.query(User).filter(User.username == "u1").first()
role = db.session.query(Role).filter(Role.name == "r1").first()
assoc = UserDevice(user=user, device=device, role=role)
db.session.add(assoc)
db.session.commit()
Run Code Online (Sandbox Code Playgroud)

请注意,ORM 关系不再是Device等的简单集合,而是UserDevice对象。这是一件好事:user.user_devices例如,当您迭代时,您将获得有关设备和用户在其上的角色的信息。如果您确实希望在不需要角色信息的情况下也提供更简单的集合,则可以使用associationproxy.