SQLAlchemy自定义声明基类实现自引用多对多关系报错如何解决
问题排查与修复方案
核心错误原因
错误提示已经明确说明问题:作为声明式基类的mixin类中,不能直接定义relationship这类Mapper属性,必须使用@declared_attr装饰器包装为可调用对象。另外你定义的关联表使用了独立的MetaData实例,没有和你的声明基类绑定,会导致后续建表失败。
修正后可运行代码
#!/usr/bin/env python3 # -*- coding: utf-8 -*- from sqlalchemy import MetaData, Table from sqlalchemy.ext.declarative import declarative_base, declared_attr from sqlalchemy import Column, ForeignKey from sqlalchemy.orm import relationship from sqlalchemy import String # 先定义基础MetaData,后续关联表和声明基类共用这个实例 metadata = MetaData() permissions = Table( 'permissions', metadata, Column('origin_id', String, ForeignKey('bases.id'), primary_key=True), Column('target_id', String, ForeignKey('bases.id'), primary_key=True) ) class Base: __tablename__ = 'bases' __table_args__ = { 'mysql_engine': 'InnoDB' } id = Column(String, primary_key=True) # 用@declared_attr装饰器包装relationship @declared_attr def targets(cls): return relationship( 'Base', secondary=permissions, primaryjoin=cls.id == permissions.c.origin_id, secondaryjoin=cls.id == permissions.c.target_id, backref='origins' ) # 绑定共用的metadata Base = declarative_base(cls=Base, metadata=metadata)
关键修改点说明
- 导入
declared_attr装饰器,将targets关系字段包装为类方法,每个继承子类实例化时会动态生成对应的关系属性 - 统一使用同一个MetaData实例,关联表和声明基类绑定相同的元数据,避免后续建表、迁移时关联表丢失
- 关系的join条件直接使用
cls指代当前类,不用硬编码类名字符串,避免类名修改后关系失效
内容的提问来源于stack exchange,提问作者rafamartinc
相关产品推荐
相关产品推荐

