Appengine NDB transactional classmethod Decorator

2019-07-18 15:44发布

I need to update some properties of an NDB model transactionally, and it seems sensible to make the update function a classmethod:

class Thing(ndb.Model):
    cost=ndb.IntegerProperty()
    @classmethod
    @ndb.transactional()
    def add_cost(cls, thid, amount):
        thing=cls.get_by_id(thid)
        thing.cost+=amount
        return thing

This is used thus:

# Add 25 to cost
thing=Thing.add_cost(thing.key.id(), 25)

Does it matter which order the decorators appear?

1条回答
欢心
2楼-- · 2019-07-18 16:48

Yes, the order of decorators does matter. From the PEP 318 -- Decorators for Functions and Methods:

The rationale for the order of application [16] (bottom to top) is that it matches the usual order for function-application. In mathematics, composition of functions (g o f)(x) translates to g(f(x)). In Python, @g @f def foo() translates to foo=g(f(foo).

In your case it would translate to:

classmethod(ndb.transactional(Thing.add_cost(...)))

A decorator would wrap the function it is decorating. Here, your add_cost function is wrapped by ndb.transactional so everything thing within the function happens in the context of a transaction and then the method returned by that is wrapped by classmethod which returns a descriptor object.

So, when you apply multiple decorators in a class, then decorators such as classmethod or staticmethod should be the top ones. If you change the order you would receive an TypeError: unbound method .... type of error if the other decorator doesn't accept descriptors.

You can also refer to following post for more details:

Why can @decorator not decorate a staticmethod or a classmethod?

查看更多
登录 后发表回答