如何正确构建 SQLAlchemy(声明式风格)Python 项目及其单元测试
我正在为一些网络应用程序开发大型后端。这是我的第一个 python 和 SQLAlchemy 项目,所以我对一些事情感到困惑。与 python 相比,Java 编程工具和 IDE 有点被宠坏了(无论如何我在 eclipse 中使用 pydev)。我需要有关如何构建项目和编写测试的帮助。我先描述一下情况。
在 PyDev 中,我将我的项目命名为“ProjectName”,下面我显示了我当前的文件夹/包和文件结构。
- 项目名称
- 项目名称
- __init__.py
- 一些_包
- __init__.py
- Foo.py
- 酒吧.py
- 测试
- 单元测试
- __init__.py
- 一些_包
- __init__.py
- TestFoo.py
- TestBar.py
- 加载测试
- 集成测试
- __init__.py
- 单元测试
- 项目名称
我在 SQLAlchemy 中使用声明式风格 Foo 和 Bar 是一些类,例如 Foo 扩展了 SQLAlchemy 声明性 Base,而 Bar 扩展了 Foo。在 __init__.py 中的“projectname.some_package”下,我有以下代码:
engine = create_engine('mysql+mysqldb://user:pass@localhost:3306/SomeDataBase', pool_recycle=3600)
Session = sessionmaker(bind=engine)
Base = declarative_base()
因此,Foo 导入此 Base 并扩展它,Bar 导入 Foo 并扩展它。我的第一个问题是,我应该将 Base 存储在 __init__.py 中并像我开始使用这两个类一样使用它吗?这个 create_engine 只是临时的,我想要配置文件并从那里加载它的设置,该怎么做?我应该在哪里调用 Base.metadata.create_all() ,以便它可以一次创建所有数据库表?
接下来,在测试类中,例如在 TestFoo 中,我有以下代码:
def setUp(self):
#create database tables and session object
self.engine = create_engine('mysql+mysqldb://user:pass@localhost:3306/SomeDatabase', pool_recycle=3600)
Session = sessionmaker(bind=self.engine)
Foo.metadata.create_all(bind=self.engine)
self.session = Session()
def tearDown(self):
#drop all tables and close session object
self.session.close()
meta = MetaData(self.engine)
meta.reflect()
meta.drop_all()
End 然后我在该测试类中有一些测试方法,并且运行良好。在 TestBar 类中,区别在于
Foo.metadata.create_all(bind=self.engine)
:
Bar.metadata.create_all(bind=self.engine)
当我运行 TestBar 时,它也运行良好。但是,当我选择两个测试类并运行它们时,我收到错误:
/usr/local/lib/python2.7/dist-packages/sqlalchemy/ext/declarative.py:1336: SAWarning: The classname 'Foo' is already in the registry of this declarative base, mapped to <class 'projectname.some_package.Foo.Foo'>
_as_declarative(cls, classname, cls.__dict__)
/usr/local/lib/python2.7/dist-packages/sqlalchemy/engine/default.py:330: Warning: Field 'id' doesn't have a default value
cursor.execute(statement, parameters)
这里有什么问题?我尝试使用鼻子和 pydev 跑步者运行测试并得到相同的错误。然后我尝试将数据库表创建移动到unit_tests下some_package中的__init__.py中,但我无法让它工作。另外,我对 python import 的工作原理感到困惑。例如,如果我在 TestBar 类中添加 Foo 导入,我也会收到与我已经显示的类似的错误。如何同时运行多个测试 SQLAlchemy 类的单元测试?
因此,再次提取最重要的问题:
- 如何正确构建使用 SQLAlchemy 声明式风格和单元测试的 python 项目。顺便说一句,我在 Foo 和 Bar 中有许多与数据库交互的类方法,在它们各自的类的上下文中,我希望这样可以吗?
- 在哪里存储基本声明类以及如何在整个项目中正确使用它,以及如何在项目中的任何位置提取所有数据库架构(我在类中以声明方式定义)并使用它?
- 如何最好地使用 SQLAlchemy 的单元测试并同时运行多个单元测试?
- 如果您还有其他建议,请随时添加?
非常感谢您的帮助。
I am developing large backend for some web apps. This is my first python and SQLAlchemy project, so I am confused with some things. And was kind of spoiled by Java programming tools and IDE`s, compared to python`s (I use pydev in eclipse anyway). I need help with how to structure the project and write tests. I`ll describe situation first.
In PyDev i named my project for example "ProjectName", and below I have shown my current folder/package and files structure.
- ProjectName
- projectname
- __init__.py
- some_package
- __init__.py
- Foo.py
- Bar.py
- tests
- unit_tests
- __init__.py
- some_package
- __init__.py
- TestFoo.py
- TestBar.py
- load_tests
- integration_tests
- __init__.py
- unit_tests
- projectname
I use declarative style in SQLAlchemy. Foo and Bar are some classes, such that Foo extends SQLAlchemy declarative Base and Bar extends Foo. Under 'projectname.some_package' in it`s __init__.py I have this code :
engine = create_engine('mysql+mysqldb://user:pass@localhost:3306/SomeDataBase', pool_recycle=3600)
Session = sessionmaker(bind=engine)
Base = declarative_base()
So, Foo imports this Base and extends it, and Bar imports Foo and extends it. My first question is, should I store Base in that __init__.py and use it like I started with this 2 classes? This create_engine is just temporary there, I would like to have config file and load it`s settings from there, how to do that? Where should I call Base.metadata.create_all(), so it can create all database tables at once?
Next, in testing classes, for example in TestFoo I have this code :
def setUp(self):
#create database tables and session object
self.engine = create_engine('mysql+mysqldb://user:pass@localhost:3306/SomeDatabase', pool_recycle=3600)
Session = sessionmaker(bind=self.engine)
Foo.metadata.create_all(bind=self.engine)
self.session = Session()
def tearDown(self):
#drop all tables and close session object
self.session.close()
meta = MetaData(self.engine)
meta.reflect()
meta.drop_all()
End then I have some test methods in that test class and it runs fine. In TestBar class difference is that
Foo.metadata.create_all(bind=self.engine)
is :
Bar.metadata.create_all(bind=self.engine)
When I run TestBar, it also runs fine. But, when I select both test classes and run them, I get errors :
/usr/local/lib/python2.7/dist-packages/sqlalchemy/ext/declarative.py:1336: SAWarning: The classname 'Foo' is already in the registry of this declarative base, mapped to <class 'projectname.some_package.Foo.Foo'>
_as_declarative(cls, classname, cls.__dict__)
/usr/local/lib/python2.7/dist-packages/sqlalchemy/engine/default.py:330: Warning: Field 'id' doesn't have a default value
cursor.execute(statement, parameters)
What is the problem here? I tried to run tests with nose and pydev runners and get same errors. Then I tried to move database tables creation in __init__.py in some_package under unit_tests but I could not get it working. Also, I`m confused about how python import works. For example if I add Foo import in TestBar class I also get errors similar to that that I have shown already. How can I run many unit tests that test SQLAlchemy classes, all at once?
So to extract most important questions again :
- How to structure python project that uses SQLAlchemy declarative style and unittests correctly. BTW I have many class methods in Foo and Bar that interact with database, in context of their respective classes, I hope that is OK?
- Where to store Base declarative class and how to properly use it in whole project, and how to extract all database schema (that I defined declaratively in my classes) anywhere in project and use it?
- How to best use unit tests with SQLAlchemy and run multiple unittests at once?
- If u have any other suggestions feel free to add it?
Thank you very much for the help.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
快速回答(抱歉,时间不够):使用单个 MetaData 实例,而不是 Foo 和 Bar 都使用一个实例。一般来说,多个 MetaData 实例是一种您几乎不需要的高级技巧。
Quick answer (lack of time, sorry): use a single MetaData instance instead of having one for both Foo and Bar. In general multiple MetaData instances is an advanced trick that you almost never need.
后面是 zzzeek 的评论,是架构/模型的文件“__init__.py”可以使用包来代替常规的 Python 文件。它适用于 SQLAlchemy 1.4.32。
Following by zzzeek's comment, a file "__init__.py" of the schema/model package can be used, instead of regular Python file. It works on SQLAlchemy 1.4.32.