gpt4 book ai didi

python - SQLAlchemy:映射器是否更改了我的对象?

转载 作者:太空狗 更新时间:2023-10-29 20:30:51 50 4
gpt4 key购买 nike

我正在尝试使用 SQLAlchemy将我的对象存储在数据库中的版本。为此,我有一个 save(...) 函数:

#!/usr/bin/env python
# encoding: utf-8

from sqlalchemy import Column, Integer, MetaData, String, Table, create_engine
from sqlalchemy.orm import mapper, sessionmaker

class MyClass(object):
def __init__(self, title):
self.title = title
def __str__(self):
return '%s' % (self.title)

def save(object_list):
metadata = MetaData()
my_class_table = Table('my_class',
metadata,
Column('id', Integer, primary_key=True),
Column('title', String(255), nullable=False))

# everything is OK here, output:
# some title
# another title
# yet another title

for obj in object_list:
print obj

mapper(MyClass, my_class_table)

# on Linux / SQLAlchemy 0.6.8, this fails with
# Traceback (most recent call last):
# File "./test.py", line 64, in <module>
# save(my_objects)
# File "./test.py", line 57, in save
# print obj
# File "./test.py", line 11, in __str__
# return '%s' % (self.title)
# File "/usr/lib/python2.7/dist-packages/sqlalchemy/orm/attributes.py", line 167, in __get__
# return self.impl.get(instance_state(instance),
# AttributeError: 'NoneType' object has no attribute 'get'

# on Mac OSX / SQLAlchemy 0.7.5, this fails with
# Traceback (most recent call last):
# File "./test.py", line 64, in <module>
# save(my_objects)
# File "./test.py", line 57, in save
# print obj
# File "./test.py", line 11, in __str__
# return '%s' % (self.title)
# File "/Library/Python/2.7/site-packages/sqlalchemy/orm/attributes.py", line 165, in __get__
# if self._supports_population and self.key in dict_:
# File "/Library/Python/2.7/site-packages/sqlalchemy/orm/attributes.py", line 139, in __getattr__
# key)
# AttributeError: Neither 'InstrumentedAttribute' object nor 'Comparator' object has an attribute '_supports_population'

for obj in object_list:
print obj

# (more code to set up engine and session...)


if __name__ == '__main__':
my_objects = [MyClass('some title'), MyClass('another title'), MyClass('yet another title')]
save(my_objects)

在我看来,当我创建映射器时,映射器隐式地对我的对象做了一些事情,我不能再使用它们了。从我读到的in similar questions ,这并非完全未知。

这种行为是预期的吗?
我在这里有什么基本错误吗?
映射和存储我的对象的正确方法是什么?


附加信息:我在 Mac OSX 10.7.3 Lion 上使用 SQLAlchemy 0.7.5 和系统默认的 Python 2.7.1,在 Kubuntu 11.10 虚拟机上使用 SQLAlchemy 0.6.8 和系统默认的 Python 2.7.2+。


更新:SQLAlchemy 映射器似乎是 well known to change objects到“SQLAlchemy 需要”。链接文章中的解决方案是在调用 mapper(...) 之后创建对象。
虽然我已经有了有效的对象 - 但我不能再使用它们了...< br/>
如何让 SQLAlchemy 存储我的对象?

更新 2:我的印象是我误解了一些关于 ORM 的基本知识:
我认为 SQLAlchemy 映射器概念为我提供了一种方法来定义我的对象并在我的应用程序中使用它们与任何数据库分离 - 只有当我想持久化它们时,我才引入 SQLAlchemy 并让它完成映射 a 的所有繁重工作类对象到数据库表。此外,除了持久性函数 save(...)load(...).

但是,通过查看下面的第一个响应,我了解到在使用任何对象之前我必须将类映射到数据库表;这将是在我的程序的最开始。也许我在这里弄错了,但这似乎是 SQLAlchemy 的一个非常严格的设计限制——所有的松散耦合都消失了。考虑到这一点,我也没有看到首先使用映射器的好处,因为我想要的“后期耦合”在 SQLAlchemy 中在技术上似乎是不可能的,我不妨只做这种“声明式”并混合和将业务逻辑与持久性代码混合:-(

更新 3:我回到第一个问题,再次阅读 SQLAlchemy。它在 front page 上说对了:

SQLAlchemy is most famous for its object-relational mapper (ORM), an optional component that provides the data mapper pattern, where classes can be mapped to the database in open ended, multiple ways - allowing the object model and database schema to develop in a cleanly decoupled way from the beginning.

然而,根据我迄今为止的经验,SQLAlchemy 似乎根本没有兑现这一 promise ,因为它迫使我从一开始就将对象模型和数据库模式结合起来。毕竟我听说过 SQLAlchemy,我真的很难相信情况确实如此,我宁愿假设我还没有理解一些基本的东西。

我做错了什么?

更新 4:

为了完整起见,我想在创建任何业务对象之前添加完成所有映射的工作示例代码:

#!/usr/bin/env python
# encoding: utf-8

from sqlalchemy import Column, Integer, MetaData, String, Table, create_engine
from sqlalchemy.orm import mapper, sessionmaker

class MyClass(object):
def __init__(self, title):
self.title = title
def __str__(self):
return '%s' % (self.title)

def save(object_list, metadata):

engine = create_engine('sqlite:///:memory:')
metadata.create_all(engine)
Session = sessionmaker(bind=engine)
session = Session()

for obj in object_list:
try:
session.add(obj)
session.commit()
except Exception as e:
print e
session.rollback()

# query objects to test they're in there
all_objs = session.query(MyClass).all()
for obj in all_objs:
print 'object id :', obj.id
print 'object title:', obj.title


if __name__ == '__main__':

metadata = MetaData()
my_class_table = Table('my_class',
metadata,
Column('id', Integer, primary_key=True),
Column('title', String(255), nullable=False))
mapper(MyClass, my_class_table)

my_objects = [MyClass('some title'), MyClass('another title'), MyClass('yet another title')]
save(my_objects, metadata)

在我自己的(非示例)代码中,我从它自己的模块中导入 MyClass 并在单独的“对象存储库”类中进行映射,该类在中创建 MetaData它的 __init__(...) 方法并将其存储在成员中,因此 save(...)load(...) 持久性方法可以根据需要访问它。所有主要应用程序需要做的就是在一开始就创建存储库对象;这包含了 SQLAlchemy 对一个类设计的影响,而且还将业务对象和映射表定义分布到代码中的不同位置。还不确定我是否会长期坚持下去,但目前看来还行。

对像我这样的 SQLAlchemy 新手的最后快速提示:你必须始终使用同一个元数据对象,否则你会得到像 no such tableclass not mapped 这样的异常.

最佳答案

SQLAlchemy 绝对会修改映射的类。 SQLAlchemy 将此称为工具

观察:

>>> print(MyClass.__dict__)
{'__module__': '__main__', '__str__': <function __str__ at 0x106d50398>, '__dict__':
<attribute '__dict__' of 'MyClass' objects>, '__weakref__': <attribute '__weakref__' of
'MyClass' objects>, '__doc__': None, '__init__': <function __init__ at 0x106d4b848>}

>>> mapper(MyClass, my_class_table)
Mapper|MyClass|my_class
>>> print(MyClass.__dict__)
{'__module__': '__main__', '_sa_class_manager': <ClassManager of <class
'__main__.MyClass'> at 7febea6a7f40>, 'title':
<sqlalchemy.orm.attributes.InstrumentedAttribute object at 0x10fba4f90>, '__str__':
<function __str__ at 0x10fbab398>, 'id': <sqlalchemy.orm.attributes.InstrumentedAttribute
object at 0x10fba4e90>, '__dict__': <attribute '__dict__' of 'MyClass' objects>,
'__weakref__': <attribute '__weakref__' of 'MyClass' objects>, '__doc__': None, '__init__':
<function __init__ at 0x10fbab410>}

同样,普通 MyClass 实例和经过检测的 MyClass 实例之间也存在差异:

>>> prem = MyClass('premapper')
>>> mapper(MyClass, my_class_table)
Mapper|MyClass|my_class
>>> postm = MyClass('postmapper')
>>> print prem
{'title': 'premapper'}
>>> print postm
{'_sa_instance_state': <sqlalchemy.orm.state.InstanceState object at 0x10e5b6d50>, 'title': 'postmapper'}

您的 NoneType 错误是因为现在检测的 MyClass.title(已替换为 InstrumentedAttribute 描述符)正在尝试获取_sa_instance_state 属性通过 instance_state(instance)_sa_instance_state 由对象创建时经过检测的 MyClass 创建,而不是由未检测的 MyClass 创建。

仪器是必不可少的。这样做是为了使用描述符将属性访问、分配和其他重要的对象状态更改传达给映射器。 (例如,如何在不修改类以监视对象中的属性访问的情况下延迟列加载甚至集合访问?)MyClass 对象未绑定(bind)到表,但它们确实需要绑定(bind)到映射器。您可以在不更改域对象的代码 的情况下更改映射选项和表,但不一定更改您的域对象本身。 (毕竟,您可以将单个对象连接到多个映射器和多个表。)

将检测视为以主题-观察者模式透明地将“主题”实现添加到“观察者”映射器的映射类。显然,您不能在任意对象上实现主体-观察者模式——您需要使被观察对象符合 Subject 接口(interface)。

我想可以通过为它创建一个 _sa_instance_state 对象来就地检测一​​个实例(我不知道如何——我必须阅读 mapper( ) 代码),但我不明白为什么这是必要的。如果您的对象有可能被 SQLAlchemy 持久化,那么在创建该对象的任何实例之前,只需为该持久性定义映射和表。您不需要创建引擎或 session ,也不需要任何数据库来定义映射。

使用完全未检测的应用程序对象甚至可以逃脱的唯一方法是,如果您的用例非常微不足道,类似于 pickleunpickle 听写。例如,如果没有检测,您永远无法在集合属性中加载或保存相关对象。你真的不打算这样做吗?如果是这样,也许你会更高兴根本不使用 sqlalchemy.orm 而只使用底层 Expression API持久化您的实例?

关于python - SQLAlchemy:映射器是否更改了我的对象?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9535001/

50 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com