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#building-a-relationship

Можете почти да го следвате; 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, и останах да правя почти същото. Така че скоро може да имам други проблеми. lol БЛАГОДАРЯ ВИ МНОГО - person CESCO; 17.10.2015
comment
Не можеш ли да кажеш на flask-rest да го вземе от device.role.permissions вместо от device.role_permissions? Както и да е, просто свойство, което връща self.role.permissions, също би свършило работа - person ThiefMaster; 17.10.2015