Flask SQL-алхимия | MySql — несколько проблем с внешними ключами

class Role(db.Model):
    __tablename__ = 'roles'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True)
    default = db.Column(db.Boolean, default=False, index=True)
    permissions = db.Column(db.Integer)


class Devices(db.Model):
    __tablename__ = 'devices'
    id = db.Column(db.Integer, primary_key=True)
    purpose = db.Column('purpose', db.String(64))
    type = db.Column('type', db.String(64))
    name = db.Column('name', db.String(64))
    channel = db.Column('channel', db.Integer)
    role_id = db.Column('role_id', db.Integer)
    role_permissions = db.Column('role_permissions', db.Integer)
    role = db.ForeignKeyConstraint(['role_id', 'role_permissions'], ['roles.id', 'roles.permissions'])

Тогда я ожидаю, что это сработает:

dev = Devices(purpose="lights",type="tcp",name="zeus",channel=8)
role = Role.query.first()
dev.role = role
db.session.add(dev)
db.session.commit()

Но после сохранения role_id и role_permissions получают нулевое значение. Почему? Как правильно это сделать??


person CESCO    schedule 16.10.2015    source источник


Ответы (1)


Вам необходимо определить отношения в дополнение к внешнему ключу.

Внешний ключ — это просто ограничение на уровне базы данных, гарантирующее, что вы не сможете ссылаться на несуществующие строки (кроме того, он помогает SQLAlchemy установить связь, не указывая в другой раз, как связаны две таблицы).

Вы хотите это в своей модели:

class Devices(db.Model):
    __table_args__ = (db.ForeignKeyConstraint(['role_id', 'role_permissions'], ['roles.id', 'roles.permissions']),)
    # ...
    role = db.relationship('Role', backref=db.backref('devices'))

Таким образом, device.role = some_role правильно заполнит внешние ключи, и, кроме того, каждый экземпляр роли будет иметь коллекцию devices, которая даст вам доступ к связанным с ней устройствам.

В руководстве по SQLAlchemy также есть раздел об отношениях: http:/ /docs.sqlalchemy.org/en/rel

class Devices(db.Model):
    __table_args__ = (db.ForeignKeyConstraint(['role_id', 'role_permissions'], ['roles.id', 'roles.permissions']),)
    # ...
    role = db.relationship('Role', backref=db.backref('devices'))
0/orm/tutorial.html#построение-отношений

Вы можете в значительной степени следовать ему; Flask-SQLAlchemy и обычная SQLalchemy на самом деле не отличаются — Flask-SQLAlchemy просто делает многие вещи доступными через объект db, чтобы избежать их явного импорта.


Кстати, поскольку Role.id является первичным ключом, вам не нужно включать role_permissions во внешний ключ — у вас не может быть более одной роли с одним и тем же идентификатором, поскольку первичный ключ всегда уникален. Это делает вашу модель еще проще:

class Devices(db.Model):
    # ...
    role_id = db.Column('role_id', db.Integer, db.ForeignKey('roles.id'))
    role = db.relationship('Role', backref=db.backref('devices'))

Вы также можете избавиться от столбца role_permissions в вашей модели Devices (который, кстати, должен называться Device). Если вам нужны разрешения, просто получите их из роли (если они вам обычно нужны, добавьте lazy=False к внешнему ключу, тогда запрос устройства всегда будет присоединяться к таблице ролей, чтобы избежать лишних запросов)

person ThiefMaster    schedule 17.10.2015
comment
Я долго боролся с этим. Не могу отблагодарить вас достаточно - person CESCO; 17.10.2015
comment
Я делаю это, потому что мое приложение использует Flask-REST, поэтому проще иметь все данные, которые мне нужны, в одном объекте json. Я также строю отношения «многие ко многим» между пользователями и устройствами, используя модель UserDevices, и застрял, делая почти то же самое. Так что скоро у меня могут возникнуть другие проблемы. лол БОЛЬШОЕ СПАСИБО - person CESCO; 17.10.2015
comment
Разве вы не можете сказать flask-rest, чтобы он брал его из device.role.permissions вместо device.role_permissions? В любом случае, простое свойство, возвращающее self.role.permissions, тоже подойдет - person ThiefMaster; 17.10.2015