我想为我的Order
模型实现状态机,并且我正在使用这个漂亮的pytransitions库。但是我面临着这个奇怪的问题。
这是我的order.py
,型号:
from order_state_machine import OrderStateMachine
class Order(BaseModel):
def __init__(self, order_dict):
super().__init__(order_dict)
# next line basically creates Customer object inside Order model
self.set('customer', Customer(order_dict['customer']))
self.machine = OrderStateMachine(self)
这是我的order_state_machine.py
:
from transitions import Machine
class OrderStateMachine(Machine):
order_states = ['pending', 'paid', 'shipped', 'delivered', 'canceled']
order_transitions = [
{'trigger': 'pay', 'source': 'pending', 'dest': 'paid'},
{'trigger': 'deliver', 'source': 'shipped', 'dest': 'delivered'},
{'trigger': 'cancel', 'source': 'shipped', 'dest': 'canceled'},
]
def __init__(self, order):
super().__init__(
model=order,
states=OrderStateMachine.order_states,
transitions=OrderStateMachine.order_transitions,
initial='pending'
)
当我这样做时:
from order import Order
new_order = Order(order_dict)
new_order.state # returns 'pending'
new_order.pay()
new_order.state # I expect 'paid'
new_order.pay()
行,给我TypeError: 'NoneType' object is not callable
错误。还有Model already contains an attribute 'trigger'. Skip binding.
警告,以及许多类似这样的警告。
[有人可以帮助我解决这个问题,可能是图书馆的维护者。谢谢。
我无法百分百确定,但是我假设您的BaseModel
已经包含与transitions
要动态修饰模型的方法重叠的属性和方法。
transitions
将执行“已检查”分配,这意味着仅当现有属性或方法尚未使用所需名称时,才将触发器和便利功能分配给模型。其背后的原因是,有时人们不关心触发器和仅按其名称调用函数(实际上使用trigger
方法)。
我假设您的BaseModel
包含与[1]类似的属性分配,如下所示。
from transitions import Machine
import logging
class BaseModel:
def __init__(self, order_dict):
self.pay = None # [1] already defined attribute
def trigger(self): # [2] already defined method
pass
class OrderStateMachine(Machine):
order_states = ['pending', 'paid', 'shipped', 'delivered', 'canceled']
order_transitions = [
{'trigger': 'pay', 'source': 'pending', 'dest': 'paid'},
{'trigger': 'deliver', 'source': 'shipped', 'dest': 'delivered'},
{'trigger': 'cancel', 'source': 'shipped', 'dest': 'canceled'},
]
def __init__(self, order):
super().__init__(
model=order,
states=OrderStateMachine.order_states,
transitions=OrderStateMachine.order_transitions,
initial='pending'
)
class Order(BaseModel):
def __init__(self, order_dict):
super().__init__(order_dict)
self.machine = OrderStateMachine(self)
logging.basicConfig(level=logging.DEBUG)
new_order = Order({}) # [2] will cause 'Model already contains an attribute 'trigger'. Skip binding.'
new_order.state # returns 'pending'
new_order.pay() # [1] will cause a TypeError: 'NoneType' object is not callable
new_order.state
为了解决这个问题,您应该确保触发器名称和模型属性是互斥的,以防止命名冲突。如果确实定义了所有这些方法(例如,将一些代码完成提示放到了IDE中),并且您确实想保持这种方式,则可以覆盖Machine._checked_assignment
方法:
class OveriddingMachine(Machine):
# assign everything to the model ignoring already existing attributes
def _checked_assignment(self, model, name, func):
setattr(model, name, func)
请注意,如果这样做,您的机器可能会以不希望的方式与模型混淆。