模型已包含属性 'trigger'。跳过绑定

Model already contains an attribute 'trigger'. Skip binding

我想为我的 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 将执行 'checked' 赋值,这意味着它只会在所需名称尚未被现有属性或方法采用时将触发器和便利函数分配给模型。这背后的原因是,有时人们不关心触发器并仅通过它们的名称调用函数(实际上使用 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)
# [2] will cause 'Model already contains an attribute 'trigger'. Skip binding.'
new_order = Order({})  
new_order.state  # returns 'pending'
# [1] will cause a TypeError: 'NoneType' object is not callable
new_order.pay()
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)

请注意,如果您这样做,您的机器可能会以不希望的方式干扰您的模型。