Многие ко многим с тремя таблицами, связанными друг с другом (SqlAlchemy) - PullRequest
0 голосов
/ 22 октября 2018

У меня есть три таблицы: пользователь, устройство и роль.Я создал отношение «многие ко многим» ч / б «Пользователь и устройство» следующим образом:

#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)

Это работает тихо, хорошо.Я могу назначить устройство d1 пользователю u1> d1.users.append(u1), а пользователя устройству> u1.devices.append(d1) и db.session.commit().

Что мне нужно, так это расширить таблицу user_devices наеще один столбец как role_id, который будет ForeignKey для таблицы ролей.Так что эта таблица 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")

Таким образом, как я могу назначить роль r1 пользователю u1 на устройстве 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)

Это создает две строки в таблице user_devices

enter image description here

Есть ли способчто мы могли бы добавить два атрибута в таблицу, как это?;

deviceRow.users.append(userRow).roles.append(roleRow)

, чтобы она создавала только одну строку после commit ()?

1 Ответ

0 голосов
/ 22 октября 2018

Ассоциация из трех сущностей больше не является простым отношением многих ко многим.Вам нужен шаблон объект ассоциации.Чтобы упростить обработку ассоциации, отобразите ее как класс модели вместо простого 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")

Чтобы связать пользователя с устройством и ролью, создайте новый объект 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()

Обратите внимание, что отношения ORM больше не являются простыми коллекциями Device и т. Д., А объектами UserDevice.Это хорошо: когда вы, например, перебираете user.user_devices, вы получаете информацию как об устройстве, так и о роли пользователя на нем.Если вы хотите предоставить более простые коллекции также для ситуаций, когда вам, например, не нужна информация о роли, вы можете использовать associationproxy.

...