跨文件的SQLAlchemy类

115 投票
5 回答
49749 浏览
提问于 2025-04-17 02:38

我正在尝试弄清楚如何将SQLAlchemy的类分散到多个文件中,但我实在搞不明白。由于我对SQLAlchemy还很陌生,请原谅我如果这个问题很简单。

考虑这三个类,每个类都在各自的文件中:

A.py:

from sqlalchemy import *
from main import Base

class A(Base):
    __tablename__ = "A"
    id  = Column(Integer, primary_key=True)
    Bs  = relationship("B", backref="A.id")
    Cs  = relationship("C", backref="A.id")

B.py:

from sqlalchemy import *
from main import Base

class B(Base):
    __tablename__ = "B"
    id    = Column(Integer, primary_key=True)
    A_id  = Column(Integer, ForeignKey("A.id"))

C.py:

from sqlalchemy import *
from main import Base

class C(Base):
    __tablename__ = "C"    
    id    = Column(Integer, primary_key=True)
    A_id  = Column(Integer, ForeignKey("A.id"))

然后假设我们有一个main.py,内容大致如下:

from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship, backref, sessionmaker

Base = declarative_base()

import A
import B
import C

engine = create_engine("sqlite:///test.db")
Base.metadata.create_all(engine, checkfirst=True)
Session = sessionmaker(bind=engine)
session = Session()

a  = A.A()
b1 = B.B()
b2 = B.B()
c1 = C.C()
c2 = C.C()

a.Bs.append(b1)
a.Bs.append(b2)    
a.Cs.append(c1)
a.Cs.append(c2)    
session.add(a)
session.commit()

上面的代码会出现错误:

sqlalchemy.exc.NoReferencedTableError: Foreign key assocated with column 'C.A_id' could not find table 'A' with which to generate a foreign key to target column 'id'

我该如何在这些文件之间共享声明基础呢?

考虑到我可能会在这个基础上添加一些像PylonsTurbogears这样的框架,完成这个的“正确”方式是什么?

编辑于2011年10月3日

我找到了一些来自Pyramids框架的描述,这个链接说明了这个问题,更重要的是,它确认这确实是一个实际的问题,而不仅仅是我自己困惑。希望这能帮助那些敢于走这条危险道路的人 :)

5 个回答

8

我正在使用 Python 2.7 + Flask 0.10 + SQLAlchemy 1.0.8 + Postgres 9.4.4.1。

这个基础模板里已经配置好了一个用户(User)和用户详情(UserDetail)模型,它们都存放在同一个文件 "models.py" 里,属于 "user" 模块。这两个类都是从 SQLAlchemy 的一个基础类继承过来的。

我在项目中添加的其他类也都是从这个基础类派生出来的。随着 models.py 文件越来越大,我决定把这个文件拆分成每个类一个文件,但在这个过程中遇到了问题。

我找到的解决办法,和 @computermacgyver 在 2013 年 10 月 23 日的帖子类似,就是把我所有的类都放到我新建的模块的 init.py 文件里,这个模块是用来存放我新创建的类文件的。看起来是这样的:

/project/models/

__init__.py contains

from project.models.a import A 
from project.models.b import B
etc...
24

我也想分享一下我的经验,因为我遇到过同样的问题。你需要在创建了 Base = declarative_base() 之后,才在文件中导入相关的类,这些类是你用来创建 BaseTables 的。下面是我项目的简单结构示例:

model/user.py

from sqlalchemy import *
from sqlalchemy.orm import relationship

from model import Base

class User(Base):
     __tablename__ = 'user'

    id = Column(Integer, primary_key=True)
    budgets = relationship('Budget')

model/budget.py

from sqlalchemy import *

from model import Base

class Budget(Base):
    __tablename__ = 'budget'

    id = Column(Integer, primary_key=True)
    user_id = Column(Integer, ForeignKey('user.id'))

model/__init__.py

from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

_DB_URI = 'sqlite:///:memory:'
engine = create_engine(_DB_URI)

Base = declarative_base()
Base.metadata.create_all(engine)
DBSession = sessionmaker(bind=engine)
session = DBSession()

from .user import User
from .budget import Budget
116

解决你问题的最简单方法就是把 Base 从导入 ABC 的模块中拿出来;也就是打破循环导入。

base.py

from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()

a.py

from sqlalchemy import *
from base import Base
from sqlalchemy.orm import relationship

class A(Base):
    __tablename__ = "A"
    id  = Column(Integer, primary_key=True)
    Bs  = relationship("B", backref="A.id")
    Cs  = relationship("C", backref="A.id")

b.py

from sqlalchemy import *
from base import Base

class B(Base):
    __tablename__ = "B"
    id    = Column(Integer, primary_key=True)
    A_id  = Column(Integer, ForeignKey("A.id"))

c.py

from sqlalchemy import *
from base import Base

class C(Base):
    __tablename__ = "C"    
    id    = Column(Integer, primary_key=True)
    A_id  = Column(Integer, ForeignKey("A.id"))

main.py

from sqlalchemy import create_engine
from sqlalchemy.orm import relationship, backref, sessionmaker

import base


import a
import b
import c

engine = create_engine("sqlite:///:memory:")
base.Base.metadata.create_all(engine, checkfirst=True)
Session = sessionmaker(bind=engine)
session = Session()

a1 = a.A()
b1 = b.B()
b2 = b.B()
c1 = c.C()
c2 = c.C()

a1.Bs.append(b1)
a1.Bs.append(b2)    
a1.Cs.append(c1)
a1.Cs.append(c2)    
session.add(a1)
session.commit()

在我的电脑上可以正常运行:

$ python main.py ; echo $?
0

撰写回答