pony中有6个钩子函数,可以在对应的时间发生时被触发.最类似的行为就是数据库中的触发器
下面以实例演示一下:
有2个类Solider(战士)和Gun(枪).逻辑是这样的:
我们打算使用2个钩子函数来完成这个功能
class Solider(db.Entity):
"""士兵"""
name = Required(str, max_len=40) # 名字
gun = Optional("Gun", column="gun_id", nullable=True, default=None) # 武器
@classmethod
@db_session
def create(cls, **kwargs) -> db.Entity:
instance = cls(**kwargs)
return instance
@db_session
def before_insert(self):
"""
:return:
"""
if self.gun is None:
gun = Gun()
gun.flush()
self.gun = gun
@db_session
def after_insert(self):
"""
:return:
"""
self.gun.sign = "{}的{}".format(self.name, self.gun.model)
class Gun(db.Entity):
"""枪"""
model = Optional(str, max_len=40, default="AK-47") # 型号
sign = Optional(str, max_len=128, nullable=True, default=None) # 铭牌
solider = Optional(Solider, nullable=True, default=None) # 士兵
if __name__ == "__main__":
solider = Solider.create(name="Tom")
print(solider.to_dict())
print(solider.gun.to_dict())
输出结果
Connected to pydev debugger (build 192.6603.34)
{'id': 1, 'name': 'Tom', 'gun': 1}
{'id': 1, 'model': 'AK-47', 'sign': 'Tom的AK-47', 'solider': 1}
Process finished with exit code 0
可以看到,我们仅仅执行了一个create方法创建实例。整个业务的过程大致如下:
这样,我们就可以创建一个实例的逻辑只写在create函数里,在before_insert和after_insert完成其他逻辑,实际业务,经常用来说关联对象的更新和实例合法性检查等。并且。所有的钩子函数都是原子性的,也就是说,只要调用的函数链中有一个进行了回滚操作或者抛出异常。整个函数链的操作将全部被取消。这大大的简化了我们平时进行数据库事务操作的复杂性。我们举个例子进行说明:
我们修改Gun.sign,加上一个unique=True的字段约束(铭牌上的签名必须唯一).
class Gun(db.Entity):
"""枪"""
model = Optional(str, max_len=40, default="AK-47") # 型号
sign = Optional(str, max_len=128, unique=True, default="") # 铭牌
solider = Optional(Solider, nullable=True, default=None) # 士兵
然后再执行如下代码:
if __name__ == "__main__":
# db.drop_table(table_name="solider", if_exists=True, with_all_data=True) # 删除表,演示实体声明时用于快速清除旧表
# db.drop_table(table_name="gun", if_exists=True, with_all_data=True) # 删除表,演示实体声明时用于快速清除旧表
db.generate_mapping(create_tables=True) # 生成实体,表和映射关系
# with db_session(sql_debug=True):
# solider = Solider(name="张三")
try:
solider = Solider.create(name="Tom")
except Exception as e:
ms = "执行异常,错误原因: {}".format(e.args[1])
print(ms)
finally:
with db_session:
for solider in select(x for x in Solider):
print(solider.to_dict())
for gun in select(x for x in Gun):
print(gun.to_dict())
我们在数据库已有名为Tom的Solider对象后,再次添加一个name=Tom的Solider对象,由于Solider的定义中 ,name是不检查唯一性的,所以这个阶段的Solider和Gun对象写入数据库没有问题。但是在函数链的最后一步骤,after_insert这个函数需要修改刚刚生成的Solider对象对应的Gun对象的sign属性。sign属性有唯一性约束( unique=True),这会导致执行出错。pony检测到出错后,直接rollback到函数链的开始处,刚才的Solider和Gun对象的插入操作作废。这一系列操作,完全不用我们操心,pony默认处理的就很好。
执行结果
插入Solider实例时,抛出了对象重复的错误(Duplicate entry),接下来我们查询数据库中的目标,可以看到Solider和Gun对象的插入都取消了。
Connected to pydev debugger (build 192.6603.34)
执行异常,错误原因: Duplicate entry 'Tom的AK-47' for key 'sign'
{'id': 1, 'name': 'Tom', 'gun': 1}
{'id': 1, 'model': 'AK-47', 'sign': 'Tom的AK-47', 'solider': 1}
Process finished with exit code 0
灵活运用6个钩子函数,你可以在保持主业务代码清晰的同时(主函数写主业务代码),实现强大的辅助功能(钩子函数写辅助业务代码),pony用自己的事务嵌套功能在后台保证了整个函数链涉及的所有数据库操作的原子性。这是一个非常强大的功能。请用好他。唯一一个需要提醒的是,处理业务逻辑,小心避免递归环路。
由于pony在实现类继承的时候采用的是单表继承的模式,所以你就没法采用写一个公共的基类,然后其他类来继承这个公共的基类进行方法扩展了。基于这种情况,pony实际上是采用多继承的方式来实现实体方法的扩展的。我们下面用实例进行说明.
class EntityMethods(object):
"""实体方法扩展类"""
@classmethod
@db_session
def create(cls, **kwargs) -> db.Entity:
"""
创建一个实例对象
:param kwargs:
:return:
"""
entity = cls(**kwargs)
entity.flush()
return entity
def digest(self) -> str:
ms = "my model is {}, i am a {}.".format(self.model, self.__class__.__name__)
return ms
class Car(db.Entity, EntityMethods):
model = Required(str, max_len=40)
class Truck(db.Entity, EntityMethods):
model = Required(str, max_len=40)
if __name__ == "__main__":
db.generate_mapping(create_tables=True) # 生成实体,表和映射关系
car = Car.create(model="BMW i351")
truck = Truck.create(model="东风 141")
print(car.digest())
print(truck.digest())
pass
执行结果
Connected to pydev debugger (build 192.6603.34)
my model is BMW i351, i am a Car.
my model is 东风 141, i am a Truck.
Process finished with exit code 0
至此,ponyorm的基本使用方法介绍完毕,希望大家玩的开心,再见。??????