【问题标题】:SQLAlchemy classes across files跨文件的 SQLAlchemy 类
【发布时间】:2023-03-23 00:35:01
【问题描述】:

我正试图弄清楚如何让 SQLAlchemy 类分布在多个文件中,但我一生都无法弄清楚如何去做。我对 SQLAlchemy 很陌生,所以如果这个问题是微不足道的,请原谅我..

各自的文件中考虑这 3 个类

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"))

然后说我们有一个类似这样的 ma​​in.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 之类的东西,实现此目的的“正确”方法是什么?

编辑 10-03-2011

我从 Pyramids 框架中找到了 this 描述,该描述描述了问题,更重要的是验证这是一个实际问题,而不仅仅是(仅)我困惑的自我是问题所在。希望它可以帮助其他敢于走这条危险道路的人:)

【问题讨论】:

  • @S.Lott 如果所有类都在一个文件中,上述方法有效,所以你告诉我:)
  • 您的代码没有出现此错误,请发布有实际错误的代码。修复你的导入,让它运行,这样别人才能真正看到你的错误。
  • @S.Lott 我的困惑显然​​集中在如何避免循环导入。我来自C,这不是问题。很抱歉占用您的时间。
  • @joveha:什么?您遇到的这些循环导入问题是什么。请发布带有循环导入的代码,以便我们解释如何分解它们并避免循环。这些 cmets 中有太多模糊的假设。你有什么问题?请具体。

标签: python sqlalchemy


【解决方案1】:

解决问题的最简单方法是将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

【讨论】:

  • 使用scoped_session
  • @user:会话处理与这篇文章中的问题无关,这实际上是一个普通的 Python 问题(我如何导入东西?);但既然我已经引起了你的注意,我建议强烈反对使用scoped_session,除非你知道为什么需要线程本地存储;使用scoped_session 的问题在于,它很容易以泄露的事务和陈旧的数据结束,而没有明确链接到代码中可能发生这种情况的点。
  • 这种设计模式似乎不适用于 python3。有没有兼容python3的简单修复?
  • @computermacgyver:这个模式应该可以跨 python 版本正常工作。请提出一个新问题,以便您可以包含所有您的代码以及您看到的错误。
  • 感谢@dequestarmappartialsetattr。我发现该错误仅在我尝试将 a.py、b.py、c.py 和 model.py 放入单独的模块时发生。我发现这种情况下的解决方案是将 base.py 代码包含在模块的 __init__.py 文件中。我已经把code and more explanation here。感谢您的回复。
【解决方案2】:

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

此样板配置了 User 和 UserDetail 模型,这些模型存储在“user”模块的同一文件“models.py”中。这些类都继承自 SQLAlchemy 基类。

我添加到项目中的所有其他类也都派生自这个基类,随着 models.py 文件越来越大,我决定将 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...

【讨论】:

  • 你认为为什么需要使用 Flask?
【解决方案3】:

如果我也有同样的问题,我也可以补充一点。在创建BaseTables 之后,您需要在创建Base = declarative_base() 的文件中导入类。如何设置我的项目的简短示例:

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'))

模型/__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

【讨论】:

  • 只有在 Python 中才需要这样的东西,但我认为这是绕过一些棘手问题的一种非常优雅的方式。唯一的区别是我需要做from . import Base 而不是from model import Base
【解决方案4】:

对我来说,在app.py 中添加import app.tool.tool_entity 和在tool/__init__.py 中添加from app.tool.tool_entity import Tool 足以创建表。不过,我还没有尝试添加关系。

文件夹结构:

app/
  app.py
  tool/
    __init__.py
    tool_entity.py
    tool_routes.py
# app/tool/tool_entity.py

from app.base import Base
from sqlalchemy import Column, Integer, String


class Tool(Base):
    __tablename__ = 'tool'

    id = Column(Integer, primary_key=True)
    name = Column(String, nullable=False)
    fullname = Column(String)
    fullname2 = Column(String)
    nickname = Column(String)

    def __repr__(self):
        return "<User(name='%s', fullname='%s', nickname='%s')>" % (
            self.name, self.fullname, self.nickname)
# app/tool/__init__.py
from app.tool.tool_entity import Tool
# app/app.py

from flask import Flask
from sqlalchemy import create_engine
from app.tool.tool_routes import tool_blueprint
from app.base import Base


db_dialect = 'postgresql'
db_user = 'postgres'
db_pwd = 'postgrespwd'
db_host = 'db'
db_name = 'db_name'
engine = create_engine(f'{db_dialect}://{db_user}:{db_pwd}@{db_host}/{db_name}', echo=True)
Base.metadata.create_all(engine)


app = Flask(__name__)
@app.route('/')
def hello_world():
    return 'hello world'


app.register_blueprint(tool_blueprint, url_prefix='/tool')

if __name__ == '__main__':
    # you can add this import here, or anywhere else in the file, as debug (watch mode) is on, 
    # the table should be created as soon as you save this file.
    import app.tool.tool_entity
    app.run(host='0.0.0.0', port=5000, debug=True)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-01-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-05
    • 1970-01-01
    • 2022-11-30
    • 2016-02-05
    相关资源
    最近更新 更多