覆盖SQLAlchemy反射表中的默认值
我在一个现有的MySQL数据库中反射了很多表。我想设置某些名字的列在任何表中默认值为当前时间(datetime.now())。但是我试着简单地遍历这些表和列,给那些我找到的特定名字的列设置默认值,却发现这样做不行。当我执行session.add(); session.flush()时,出现了以下错误:
AttributeError: 'builtin_function_or_method' object has no attribute '__visit_name__'
这个问题似乎和调用_set_parent有关,还有在sqlalchemy.schema的721行中的这段代码self._init_items(*toinit)
。
有没有人知道有没有办法做到这一点,而不需要遍历我所有的反射表,在每个地方都添加Column(..)的代码,或者使用一些很难看的黑科技呢?
3 个回答
好的,我解决了这个问题,不过这个方法有点不太好,因为我用了一个内部的方法。你需要这样做:
from sqlalchemy.schema import ColumnDefault
#....
ColumnDefault(callable)._set_parent(column)
这样就能实现你想要的效果。注意,你还可以给ColumnDefault传一个参数for_update=True,这样它的行为就会在更新时改变,而不是在插入时。
我感觉有点不太光彩 :-(
另一种选择是在反射时覆盖需要默认值的列:
Table('some_table', metadata,
Column('create_time', DateTime, default=datetime.now),
autoload=True)
不过,目前你不能同时反射数据类型和在SQLAlchemy这边设置默认值。有人可能会说,数据类型是接口定义的一部分,所以这种重复并不会造成维护上的问题——如果列的数据类型发生变化,你很可能也需要修改默认值的函数。
五年后,你可以使用事件监听器。你可以注册一个监听器,指定一个函数来执行:
def do_this_on_column_reflect(inspector, table, column_info):
column_name = column_info.get("name")
if column_name == "create_datetime":
column_info["default"] = datetime.now()
event.listen(Table, "column_reflect", do_this_on_column_reflect)
[1] http://docs.sqlalchemy.org/en/latest/core/events.html#sqlalchemy.events.DDLEvents.column_reflect
[2] http://docs.sqlalchemy.org/en/latest/core/event.html#sqlalchemy.event.listen