Python 炼金术中的信号或触发器
SQLAlchemy是否有类似于Django的信号概念?基本上,我希望在预保存或后保存某些实体对象时触发一些函数。谢谢Python 炼金术中的信号或触发器,python,django,sqlalchemy,Python,Django,Sqlalchemy,SQLAlchemy是否有类似于Django的信号概念?基本上,我希望在预保存或后保存某些实体对象时触发一些函数。谢谢 编辑:我只想要SQLAlchemy中django信号的等价物。您没有弄清楚,是集成SQLAlchemy和django,还是只想要SQLAlchemy中django信号的等价物 如果你想要类似于Django的信号,比如post_save、pre_save、pre_delete等,我会在页面上给你介绍 < P>你可能想考虑一下 下面是我编写的一些代码,用于在实例上设置所有者id
编辑:我只想要SQLAlchemy中django信号的等价物。您没有弄清楚,是集成SQLAlchemy和django,还是只想要SQLAlchemy中django信号的等价物 如果你想要类似于Django的信号,比如post_save、pre_save、pre_delete等,我会在页面上给你介绍
< P>你可能想考虑一下 下面是我编写的一些代码,用于在实例上设置所有者id,并设置更新日期,以便在pylons应用程序中完成任务。OrmExt类是所有魔法发生的地方。而init_模型就是把它连接起来的地方
import logging
import sqlalchemy as sa
from sqlalchemy import orm
from pylons import session
import datetime
log = logging.getLogger(__name__)
class ORMSecurityException(Exception):
'''
thrown for security violations in orm layer
'''
pass
def _get_current_user():
log.debug('getting current user from session...')
log.debug(session)
return session['user']
def _is_admin(user):
return False
def set_update_date(instance):
if hasattr(instance,'update_date'):
instance.update_date = datetime.datetime.now()
def set_owner(instance):
'''
if owner_id, run it through the rules
'''
log.info('set_owner')
if hasattr(instance, 'owner_id'):
log.info('instance.owner_id=%s' % instance.owner_id)
u = _get_current_user()
log.debug('user: %s' % u.email)
if not u:
#anonymous users can't save owned objects
raise ORMSecurityException()
if instance.owner_id==None:
#must be new object thus, owned by current user
log.info('setting owner on object %s for user: %s' % (instance.__class__.__name__,u.email))
instance.owner_id = u.id
elif instance.owner_id!=u.id and not _is_admin(u):
#if owner_id does not match user_id and user is not admin VIOLATION
raise ORMSecurityException()
else:
log.info('object is already owned by this user')
return #good to go
else:
log.info('%s is not an owned object' % instance.__class__.__name__)
return
def instance_policy(instance):
log.info('setting owner for %s' % instance.__class__.__name__)
set_owner(instance)
log.info('setting update_date for %s' % instance.__class__.__name__)
set_update_date(instance)
class ORMExt(orm.SessionExtension):
'''
attempt at managing ownership logic on objects
'''
def __init__(self,policy):
self._policy = policy
def before_flush(self,sqlsess,flush_context,instances):
'''
check all instances for owner_id==user.id
'''
try:
for instance in sqlsess.deleted:
try:
log.info('running policy for deleted %s' % instance.__class__.__name__)
self._policy(instance)
except Exception,ex:
log.error(ex)
raise ex
for instance in sqlsess.new:
try:
log.info('running policy for new %s' % instance.__class__.__name__)
self._policy(instance)
except Exception,ex:
log.error(ex)
raise ex
for instance in sqlsess.dirty:
try:
if sqlsess.is_modified(instance,include_collections=False,passive=True):
log.info('running policy for updated %s' % instance.__class__.__name__)
self._policy(instance)
except Exception, ex:
log.error(ex)
raise ex
except Exception,ex:
sqlsess.expunge_all()
raise ex
def init_model(engine):
"""Call me before using any of the tables or classes in the model"""
sm = orm.sessionmaker(autoflush=True, autocommit=True, bind=engine,extension=ORMExt(instance_policy))
meta.engine = engine
meta.Session = orm.scoped_session(sm)
以下是我对这个问题的看法,它用来发送信号:
dispatch.py
"""
Signals dispatching for SQLAlchemy mappers.
"""
import louie
from sqlalchemy.orm.interfaces import MapperExtension
import signals
class LouieDispatcherExtension(MapperExtension):
"""
Dispatch signals using louie on insert, update and delete actions.
"""
def after_insert(self, mapper, connection, instance):
louie.send(signals.after_insert, instance.__class__,
instance=instance)
return super(LouieDispatcherExtension, self).after_insert(mapper,
connection, instance)
def after_delete(self, mapper, connection, instance):
louie.send(signals.after_delete, instance.__class__,
instance=instance)
return super(LouieDispatcherExtension, self).after_delete(mapper,
connection, instance)
def after_update(self, mapper, connection, instance):
louie.send(signals.after_update, instance.__class__,
instance=instance)
return super(LouieDispatcherExtension, self).after_update(mapper,
connection, instance)
def before_delete(self, mapper, connection, instance):
louie.send(signals.before_delete, instance.__class__,
instance=instance)
return super(LouieDispatcherExtension, self).before_delete(mapper,
connection, instance)
def before_insert(self, mapper, connection, instance):
louie.send(signals.before_insert, instance.__class__,
instance=instance)
return super(LouieDispatcherExtension, self).before_insert(mapper,
connection, instance)
def before_update(self, mapper, connection, instance):
louie.send(signals.before_update, instance.__class__,
instance=instance)
return super(LouieDispatcherExtension, self).before_update(mapper,
connection, instance)
from louie import Signal
class after_delete(Signal): pass
class after_insert(Signal): pass
class after_update(Signal): pass
class before_delete(Signal): pass
class before_insert(Signal): pass
class before_update(Signal): pass
信号.py
"""
Signals dispatching for SQLAlchemy mappers.
"""
import louie
from sqlalchemy.orm.interfaces import MapperExtension
import signals
class LouieDispatcherExtension(MapperExtension):
"""
Dispatch signals using louie on insert, update and delete actions.
"""
def after_insert(self, mapper, connection, instance):
louie.send(signals.after_insert, instance.__class__,
instance=instance)
return super(LouieDispatcherExtension, self).after_insert(mapper,
connection, instance)
def after_delete(self, mapper, connection, instance):
louie.send(signals.after_delete, instance.__class__,
instance=instance)
return super(LouieDispatcherExtension, self).after_delete(mapper,
connection, instance)
def after_update(self, mapper, connection, instance):
louie.send(signals.after_update, instance.__class__,
instance=instance)
return super(LouieDispatcherExtension, self).after_update(mapper,
connection, instance)
def before_delete(self, mapper, connection, instance):
louie.send(signals.before_delete, instance.__class__,
instance=instance)
return super(LouieDispatcherExtension, self).before_delete(mapper,
connection, instance)
def before_insert(self, mapper, connection, instance):
louie.send(signals.before_insert, instance.__class__,
instance=instance)
return super(LouieDispatcherExtension, self).before_insert(mapper,
connection, instance)
def before_update(self, mapper, connection, instance):
louie.send(signals.before_update, instance.__class__,
instance=instance)
return super(LouieDispatcherExtension, self).before_update(mapper,
connection, instance)
from louie import Signal
class after_delete(Signal): pass
class after_insert(Signal): pass
class after_update(Signal): pass
class before_delete(Signal): pass
class before_insert(Signal): pass
class before_update(Signal): pass
示例用法:
class MyModel(DeclarativeBase):
__mapper_args__ = {"extension": LouieDispatcherExtension()}
ID = Column(Integer, primary_key=True)
name = Column(String(255))
def on_insert(instance):
print "inserted %s" % instance
louie.connect(on_insert, signals.after_insert, MyModel)
我想你在找“ORM事件”。 您可以在此处找到文档:
您可以使用内部
MapperExtension
类:
class YourModel(db.Model):
class BaseExtension(MapperExtension):
def before_insert(self, mapper, connection, instance):
# do something here
def before_update(self, mapper, connection, instance):
# do something here
__mapper_args__ = { 'extension': BaseExtension() }
# ....
简单地重写
save
,有什么不对?@s.Lott很抱歉,要复活一个古老的线程,但是SQLAlchemy声明性模块没有像Django ORM那样可以重写的save
方法的相同概念。要在SA中保存对象,请将它们添加到会话中,然后刷新会话。不过,您可以实现自己的声明性基类并实现相同的效果。@JoeHolloway:虽然很有帮助,但它没有解释为什么标签上写着Django,标题上写着SQLAlchemy.BTW,MapperExtension
来自其他答案,是因为SQLAlchemy 0.7。所以“ORM事件”看起来是一个合适的解决方案。请参阅S.C.关于“ORM事件”的答案,了解当前的解决方法。从SqLalCyMy文档:“新版本0.7:事件取代了以前的“扩展”类系统。”所有这些事件都有一个问题,它们已经处于刷新过程的中间。所以,虽然修改实例上的列已经足够了,但如果您想要执行更复杂的操作,例如从会话中添加/删除实例,则这是不够的。所有这些更改都将被丢弃。我可以用它来创建一个触发器,在插入时在另一个表中插入一行吗?类似于用作日志的表。