在App Engine上,如何设置ndb属性不可变?

时间:2013-12-19 02:45:25

标签: google-app-engine python-2.7 google-cloud-datastore app-engine-ndb

这似乎是一个普通的要求,它是内置的,但无论如何:如果你有一个类似下面的模型,你如何阻止eggs属性在被设置后被变异?

class Spam(ndb.Model):

    eggs = ndb.StringProperty()

目标是要求属性,因此默认为None,但一旦从None变异,就会变为在上面的情况下,它永远不会再被改变,但是对于定义不可变属性的任何见解都将受到赞赏。

我曾考虑使用validator的{​​{1}}参数传递函数;请参阅下面的答案,了解为什么在这里不起作用。它对于理解所涉及的对象和命名空间很有用。

2 个答案:

答案 0 :(得分:2)

一种不要求您使用自定义属性的方法是使用钩子请参阅文档https://developers.google.com/appengine/docs/python/ndb/modelclass#Model__post_get_hook

您可以使用_post_get_hook(cls, key, future)_pre_put_hook(self)

_post_get_hook中,您将存储该属性的原始值 并且在_pre_put_hook中,除非原始值为None,否则您将检查它是否与原始值相同。

class Spam(ndb.Model):

    eggs =  ndb.StringProperty()

    @classmethod
    def _post_get_hook(cls,key,future):
        obj = future.get_result()
        obj._my_eggs_prop = obj.eggs

    def _pre_put_hook(self):
        if hasattr(self,'_my_eggs_prop'):
            if self.eggs != self._my_eggs_prop:
               if self._my_eggs_prop != None:
                    # do some logging.
                    raise ValueError
        else:
            setattr(self,'_my_eggs_prop',self.eggs)
            # if the saved value doesn't exist, create it and store
            # the value in case an update occurs after the initial put
            # this also means the object was created and not get() 

这是一个工作的例子

s~lightning-catfish> import spam
s~lightning-catfish> x = spam.Spam(id='canned')
s~lightning-catfish> x.eggs = 'green'
s~lightning-catfish> x.put()
Key('Spam', 'canned')
s~lightning-catfish> y = x.key.get()
s~lightning-catfish> y._my_eggs_prop
u'green'
s~lightning-catfish> y.eggs = 'blue'
s~lightning-catfish> y.put()
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/home/timh/google_appengine/google/appengine/ext/ndb/model.py", line 3232, in _put
    return self._put_async(**ctx_options).get_result()
  File "/home/timh/google_appengine/google/appengine/ext/ndb/model.py", line 3247, in _put_async
    self._pre_put_hook()
  File "spam.py", line 18, in _pre_put_hook
    raise ValueError
ValueError

这种方法的缺点是你可以改变属性依赖它来获得一些额外的代码,然后在你做put时才发现它。然而,这可能不是那么糟糕,因为理论上你应该没有任何代码在改变后修改属性。所以你想记录,并追踪它是如何发生的。或者,您可以将值重置为原始设置,但随后会保留错误的代码。

自定义属性需要更多考虑; - )

答案 1 :(得分:1)

不幸的是,你无法用验证器做你想做的事。

(prop,value)是要设置的属性实例和值。您在类的实例的验证器中没有句柄,因此没有预先存在的值。获取现有值所需的属性的所有方法都需要一个模型实例作为参数 - 比如_has_value。文档

  

将使用参数(prop,value)调用,并且应该返回   (可能是强制的)值或引发异常。打电话给   再次对强制值起作用不应该进一步修改该值。   (例如,返回value.strip()或value.lower()很好,但是   不值+'$'。)也可以返回None,这意味着“没有变化”。看到   还写属性子类   `https://developers.google.com/appengine/docs/python/ndb/properties#options

您需要编写自定义属性来管理值的状态并防止一旦设置覆盖。

请参阅下面的示例,向您显示您无法访问该属性的预先存在的值,并且未传递验证程序或具有该值的模型实例。

s~lightning-catfish> from pdb import set_trace
s~lightning-catfish> def valid(prop,val):
...    set_trace()
...    return val
... 
s~lightning-catfish> class X(ndb.Model):
...    x = ndb.StringProperty(validator=valid)
... 
s~lightning-catfish> y = X(x="abc")
> <console>(3)valid()
(Pdb) p prop
StringProperty('x', validator=<function valid at 0xaf2d02c>)

(Pdb) p prop._has_value
<bound method StringProperty._has_value of StringProperty('x', validator=<function valid at 0xaf2d02c>)>
(Pdb) p prop._has_value()
*** TypeError: TypeError('_has_value() takes at least 2 arguments (1 given)',)
(Pdb) c
s~lightning-catfish> y
X(x='abc')