跨文件的SQLAlchemy类


82

我试图弄清楚如何将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之类的东西,完成此任务的“正确”方法是什么?

编辑10-03-2011

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


7
@ S.Lott如果所有类都在一个文件中,则上面的方法起作用,所以您告诉我:)
joveha 2011年

您的代码未出现此错误,请发布包含实际错误的代码。修复您的导入,使其运行,以便有人可以实际看到您的错误。
knitti 2011年

@ S.Lott我的困惑显然​​集中在如何避免周期性进口方面。我来自C,这不是问题。很抱歉占用您的时间。
joveha 2011年

@joveha:什么?您遇到的这些周期性导入问题是什么。请发布带有循环导入的代码,以便我们可以解释如何分解它们并避免循环。这些评论中有太多模糊的假设。你有什么问题?请具体说明。
S.Lott

Answers:


87

解决您的问题的最简单方法是Base取出导入的模块AB然后C;中断循环导入。

base.py

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

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

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

1
使用scoped_session
用户

3
@user:会话处理与本文中的问题无关,这实际上是一个普通的老python问题(如何导入内容?);但是,由于引起了您的注意,因此强烈建议scoped_session不要使用,除非您知道为什么需要线程本地存储;否则,不建议使用。使用的问题scoped_session在于,它可以很容易地处理泄漏的事务和过时的数据,而在发生这种情况时,没有显式链接到代码中的点。
SingleNegationElimination

这种设计模式似乎不适用于python3。有没有与python3兼容的简单修复程序?
computermacgyver

@computermacgyver:此模式应在python版本之间正常工作。请提出新问题,这样就可以包括所有代码,您所看到的错误。
SingleNegationElimination

谢谢@dequestarmappartialsetattr。我发现错误仅在尝试将a.py,b.py,c.py和model.py放入单独的模块中时发生。我发现在这种情况下,解决方案是将base.py代码包含在模块的__init__.py文件中。我把代码和更多的解释放在这里。谢谢回复。
computermacgyver

13

如果我也遇到同样的问题,我也可以增加一点理智。您需要导入,你创建的文件中的类Base = declarative_base()创建的AFTERBaseTables。简短示例如何设置我的项目:

型号/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')

型号/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

8

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

该样板配置了一个User和UserDetail模型,这些模型存储在“用户”模块的同一文件“ 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...

2
为什么您认为需要使用Flask?

0

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

资料夹结构:

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)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.