I'm trying to use the property decorator in a Class. While it works well per se, I can't use any code that has to access the REQUEST
.
class SomeClass():
#Zope magic code
_properties=({'id':'someValue', 'type':'ustring', 'mode':'r'},)
def get_someValue(self):
return self.REQUEST
@property
def someValue(self):
return self.REQUEST
Although calling get_someValue
gets me the desired result, trying to access someValue
raises an AttributeError
.
What's the logic behind this behaviour? Is there a way to get around this limitation?
(I'm using Zope 2.13.16, Python 2.7.3)
The property
decorator only works with new-style classes; that is to say, classes that inherit from object
. Acquisition (which gives you access to the global REQUEST
object via attribute access) on the other hand is very much 'old-skool' python and the two do not work well together, as property
ignores acquisition wrappers, which are needed to acquire the REQUEST
object.
Zope has it's own property
-like method that pre-dates new-style classes and the property
decorater, called ComputedAttribute
, which actually predates the property
decorator and new-style classes by many years. A ComputedAttribute
-wrapped function does know how to behave with an Acquisition
-wrapped object, though.
You can use ComputedAttibute
much like the property
decorator:
from ComputedAttribute import ComputedAttribute
class SomeClass():
@ComputedAttribute
def someProperty(self):
return 'somevalue'
The ComputedAttribute
wrapper function also can be configured with a level of wrapping, which is what we need when dealing with Acquisition wrappers. You cannot use the ComputedAttribute
as a decorator in that case:
class SomeClass():
def someValue(self):
return self.REQUEST
someValue = ComputedAttribute(someValue, 1)
It is easy enough to define a new function to do the decorating for us though:
from ComputedAttribute import ComputedAttribute
def computed_attribute_decorator(level=0):
def computed_attribute_wrapper(func):
return ComputedAttribute(func, level)
return computed_attribute_wrapper
Stick this in a utility module somewhere, after which you can then use it as a callable decorator to mark something as an Acquisition-aware property:
class SomeClass():
@computed_attribute_decorator(level=1)
def someValue(self):
return self.REQUEST
Note that unlike property
, ComputedAttribute
can only be used for getters; there is no support for setters or deleters.
If you want to route around needing acquisition and cannot explicitly set the request from calling code in the constructor of your class, use zope.globalrequest. Otherwise, you may want to consider a browser view (which always multi-adapts some context and a request).