Отношение, чтобы «прыгать» через отношения M2M в SqlAlchemy (Пользователь -> Роль -> Разрешение) - PullRequest
0 голосов
/ 06 мая 2019

Я пытаюсь смоделировать очень типичную структуру разрешений, в которой модель User (пользователь моего сайта) может быть назначена какому-то Roles, а у каждой из этих Roles есть несколько Permissions.

Было бы очень полезно, если бы я мог получить relationship от User прямо до Permission. Таким образом, я мог бы извлечь пользователя (экземпляр) из базы данных и просто сделать user.permissions, чтобы получить его разрешения, поставить некоторую фильтрацию, чтобы проверить, есть ли у пользователя конкретный Permission, предварительно загружено ... в сумме: все вкусности, которые идут с relationship.

A viewonly отношения будут в полном порядке. Как упоминал Майк Байер в очень похожем вопросе , я не могу писать в User.permissions, потому что мы не знаем, какую «роль» использовать или , где вставить это.

Я создал две промежуточные таблицы:

User -- M2M --> Role(s) -- M2M --> Permission(s)
 |                                     ^
 +-------- user.permissions -----------+
  • users_roles для подключения user к roles через их первичный ключ (идентификатор)
  • и roles_permissions для подключения каждого role к его permissions,

Это моя структура таблицы (упрощенная для вопроса, но даже полная версия действительно типичная и ... простая)

class User(DeclarativeBase, Mixin):
    __tablename__ = 'users'
    email = Column(String(25), nullable=False)

_users_roles = Table('users_roles', DeclarativeBase.metadata,
    Column('user_id', ForeignKey('users.id', ...
    Column('role_id', ForeignKey('roles.id', ...
    PrimaryKeyConstraint('user_id', 'role_id',),
)    

class Role(DeclarativeBase, Mixin):
    __tablename__ = 'roles'

    name = Column(Text(), nullable=False, unique=True)
    users = relationship("User", secondary=_users_roles, backref="roles")

_roles_permissions = Table('roles_permissions', DeclarativeBase.metadata,
    Column('role_id', ForeignKey('roles.id',  ...
    Column('permission_id', ForeignKey('permissions.id', ...
    PrimaryKeyConstraint('role_id', 'permission_id',),
)

class Permission(DeclarativeBase, Mixin):
    __tablename__ = 'permissions'

    key = Column(Text, nullable=False, unique=True,) 

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

permissions = relationship('Permission',
    secondary="""join(User, users_roles, 
                     User.id == users_roles.c.user_id
                 ).join(roles_permissions, 
                    users_roles.c.role_id == roles_permissions.c.role_id
                ).join(
                  Permission, roles_permissions.c.permission_id == Permission.id
               )""",
    viewonly=True,
)

Что дает мне эту ошибку:

sqlalchemy.exc.ArgumentError: Relationship User.permissions 
could not determine any unambiguous local/remote column pairs 
based on join condition and remote_side arguments.  Consider 
using the remote() annotation to accurately mark those elements
of the join condition that are on the remote side of the relationship.

Любые советы будут с благодарностью. Заранее спасибо.

1 Ответ

2 голосов
/ 06 мая 2019

Вторичные должны включать не сами таблицы, а только связь между ними:

permissions = relationship(
    'Permission',
    secondary="""join(users_roles, roles_permissions, 
                      users_roles.c.role_id == roles_permissions.c.role_id)""",
    viewonly=True,
)

Наличие таблиц, подлежащих объединению, на вторичном сервере приводит в замешательство автоматизацию, которая пытается найти отношения внешнего ключа между User и Permission через вторичный сервер.

...