我试图弄清楚如何映射到一个简单的只读属性,并在保存到数据库时触发该属性。
一个人为的例子应该使这一点更加清楚。首先,一个简单的表:
meta = MetaData() foo_table = Table('foo', meta, Column('id', String(3), primary_key=True), Column('description', String(64), nullable=False), Column('calculated_value', Integer, nullable=False), )
我想做的是设置一个带有只读属性的类,当我调用session.commit()时,该类将为我插入calculated_value列中。
import datetime def Foo(object): def __init__(self, id, description): self.id = id self.description = description @property def calculated_value(self): self._calculated_value = datetime.datetime.now().second + 10 return self._calculated_value
根据sqlalchemy文档,我 认为 我应该这样映射:
mapper(Foo, foo_table, properties = { 'calculated_value' : synonym('_calculated_value', map_column=True) })
这样做的问题是,在您访问calculated_value属性之前,_calculated_value为“无”。似乎SQLAlchemy在插入数据库时没有调用该属性,因此我得到了None值。什么是正确的映射方式,以便将“ calculated_value”属性的结果插入到foo表的“ calculated_value”列中?
好-如果有人有同样的问题,我正在编辑这篇文章。 我最终要做的是使用MapperExtension。让我给您一个更好的例子以及扩展的用法:
class UpdatePropertiesExtension(MapperExtension): def __init__(self, properties): self.properties = properties def _update_properties(self, instance): # We simply need to access our read only property one time before it gets # inserted into the database. for property in self.properties: getattr(instance, property) def before_insert(self, mapper, connection, instance): self._update_properties(instance) def before_update(self, mapper, connection, instance): self._update_properties(instance)
这就是您的使用方式。假设您有一个带有多个只读属性的类,这些属性必须在插入数据库之前启动。我在这里假设,对于这些只读属性中的每一个,您都希望在数据库中具有要填充属性值的相应列。您仍将为每个属性设置一个同义词,但是在映射对象时,请使用上面的mapper扩展名:
class Foo(object): def __init__(self, id, description): self.id = id self.description = description self.items = [] self.some_other_items = [] @property def item_sum(self): self._item_sum = 0 for item in self.items: self._item_sum += item.some_value return self._item_sum @property def some_other_property(self): self._some_other_property = 0 .... code to generate _some_other_property on the fly.... return self._some_other_property mapper(Foo, metadata, extension = UpdatePropertiesExtension(['item_sum', 'some_other_property']), properties = { 'item_sum' : synonym('_item_sum', map_column=True), 'some_other_property' : synonym('_some_other_property', map_column = True) })
我不确定使用sqlalchemy.orm.synonym是否可以实现所需的功能。可能没有给出这样的事实,即sqlalchemy如何跟踪哪些实例是脏的并且需要在刷新期间进行更新。
但是还有其他方法可以获取此功能-SessionExtensions(请注意,顶部需要填充的engine_string变量):
(env)zifot@localhost:~/stackoverflow$ cat stackoverflow.py engine_string = '' from sqlalchemy import Table, Column, String, Integer, MetaData, create_engine import sqlalchemy.orm as orm import datetime engine = create_engine(engine_string, echo = True) meta = MetaData(bind = engine) foo_table = Table('foo', meta, Column('id', String(3), primary_key=True), Column('description', String(64), nullable=False), Column('calculated_value', Integer, nullable=False), ) meta.drop_all() meta.create_all() class MyExt(orm.interfaces.SessionExtension): def before_commit(self, session): for obj in session: if isinstance(obj, Foo): obj.calculated_value = datetime.datetime.now().second + 10 Session = orm.sessionmaker(extension = MyExt())() Session.configure(bind = engine) class Foo(object): def __init__(self, id, description): self.id = id self.description = description orm.mapper(Foo, foo_table) (env)zifot@localhost:~/stackoverflow$ ipython Python 2.5.2 (r252:60911, Jan 4 2009, 17:40:26) Type "copyright", "credits" or "license" for more information. IPython 0.10 -- An enhanced Interactive Python. ? -> Introduction and overview of IPython's features. %quickref -> Quick reference. help -> Python's own help system. object? -> Details about 'object'. ?object also works, ?? prints more. In [1]: from stackoverflow import * 2010-06-11 13:19:30,925 INFO sqlalchemy.engine.base.Engine.0x...11cc select version() 2010-06-11 13:19:30,927 INFO sqlalchemy.engine.base.Engine.0x...11cc {} 2010-06-11 13:19:30,935 INFO sqlalchemy.engine.base.Engine.0x...11cc select current_schema() 2010-06-11 13:19:30,936 INFO sqlalchemy.engine.base.Engine.0x...11cc {} 2010-06-11 13:19:30,965 INFO sqlalchemy.engine.base.Engine.0x...11cc select relname from pg_class c join pg_namespace n on n.oid=c.relnamespace where n.nspname=current_schema() and lower(relname)=%(name)s 2010-06-11 13:19:30,966 INFO sqlalchemy.engine.base.Engine.0x...11cc {'name': u'foo'} 2010-06-11 13:19:30,979 INFO sqlalchemy.engine.base.Engine.0x...11cc DROP TABLE foo 2010-06-11 13:19:30,980 INFO sqlalchemy.engine.base.Engine.0x...11cc {} 2010-06-11 13:19:30,988 INFO sqlalchemy.engine.base.Engine.0x...11cc COMMIT 2010-06-11 13:19:30,997 INFO sqlalchemy.engine.base.Engine.0x...11cc select relname from pg_class c join pg_namespace n on n.oid=c.relnamespace where n.nspname=current_schema() and lower(relname)=%(name)s 2010-06-11 13:19:30,999 INFO sqlalchemy.engine.base.Engine.0x...11cc {'name': u'foo'} 2010-06-11 13:19:31,007 INFO sqlalchemy.engine.base.Engine.0x...11cc CREATE TABLE foo ( id VARCHAR(3) NOT NULL, description VARCHAR(64) NOT NULL, calculated_value INTEGER NOT NULL, PRIMARY KEY (id) ) 2010-06-11 13:19:31,009 INFO sqlalchemy.engine.base.Engine.0x...11cc {} 2010-06-11 13:19:31,025 INFO sqlalchemy.engine.base.Engine.0x...11cc COMMIT In [2]: f = Foo('idx', 'foo') In [3]: f.calculated_value In [4]: Session.add(f) In [5]: f.calculated_value In [6]: Session.commit() 2010-06-11 13:19:57,668 INFO sqlalchemy.engine.base.Engine.0x...11cc BEGIN 2010-06-11 13:19:57,674 INFO sqlalchemy.engine.base.Engine.0x...11cc INSERT INTO foo (id, description, calculated_value) VALUES (%(id)s, %(description)s, %(calculated_value)s) 2010-06-11 13:19:57,675 INFO sqlalchemy.engine.base.Engine.0x...11cc {'description': 'foo', 'calculated_value': 67, 'id': 'idx'} 2010-06-11 13:19:57,683 INFO sqlalchemy.engine.base.Engine.0x...11cc COMMIT In [7]: f.calculated_value 2010-06-11 13:20:00,755 INFO sqlalchemy.engine.base.Engine.0x...11cc BEGIN 2010-06-11 13:20:00,759 INFO sqlalchemy.engine.base.Engine.0x...11cc SELECT foo.id AS foo_id, foo.description AS foo_description, foo.calculated_value AS foo_calculated_value FROM foo WHERE foo.id = %(param_1)s 2010-06-11 13:20:00,761 INFO sqlalchemy.engine.base.Engine.0x...11cc {'param_1': 'idx'} Out[7]: 67 In [8]: f.calculated_value Out[8]: 67 In [9]: Session.commit() 2010-06-11 13:20:08,366 INFO sqlalchemy.engine.base.Engine.0x...11cc UPDATE foo SET calculated_value=%(calculated_value)s WHERE foo.id = %(foo_id)s 2010-06-11 13:20:08,367 INFO sqlalchemy.engine.base.Engine.0x...11cc {'foo_id': u'idx', 'calculated_value': 18} 2010-06-11 13:20:08,373 INFO sqlalchemy.engine.base.Engine.0x...11cc COMMIT In [10]: f.calculated_value 2010-06-11 13:20:10,475 INFO sqlalchemy.engine.base.Engine.0x...11cc BEGIN 2010-06-11 13:20:10,479 INFO sqlalchemy.engine.base.Engine.0x...11cc SELECT foo.id AS foo_id, foo.description AS foo_description, foo.calculated_value AS foo_calculated_value FROM foo WHERE foo.id = %(param_1)s 2010-06-11 13:20:10,481 INFO sqlalchemy.engine.base.Engine.0x...11cc {'param_1': 'idx'} Out[10]: 18
有关SessionExtensions的更多信息:sqlalchemy.orm.interfaces.SessionExtension。