0

在我的 Google App Engine 应用程序中,我有需要存储的模型对象。这些对象由各种策略对象参数化。例如,我的 Event 类有一个 Privacy Policy 对象,该对象决定谁可以查看、更新等。PrivacyPolicy 有各种不同的子类。事件在各个点咨询其 PrivacyPolicy 对象。

class PrivacyPolicy(db.Model):
    def can_see(self, event, user):
        pass

class OwnerOnlyPolicy(PrivacyPolicy):
    def can_see(self, event, user):
        return user == event.owner

class GroupOnlyPolicy(PrivacyPolicy):
    def can_see(self, event, user):
        for grp in event.owner.groups()
            if grp.is_member(user):
                return True
        return False

class OnlyCertainUsersPolicy(PrivacyPolicy):
    def __init__(self, others):
        self.others = others

    def can_see(self, event, user):
        return user in others

我可以让我的 Event 类使用 PrivacyPolicy 的 ReferenceProperty:

class Event(db.Model):
    privacy: db.ReferenceProperty(PrivacyPolicy)
    #…

我不喜欢这样的原因是一对一的关系意味着没有人每次都查询策略对象,不需要维护从策略到其事件对象的反向引用,并且没有其他方式PrivacyPolicy 是一个独立的数据库级对象。它在功能上等同于 IntegerProperty,因为它是 Event 对象状态的一部分,它只是一个对象而不是数字——特别是它是一个可以有零状态或很多状态的对象,对于 Event 类型是未知的。

我找不到任何人谈论如何处理这种情况。有没有我不知道的工具/方法?我只是把它吸起来并使用参考属性和开销吗?

如果处理此问题的唯一其他方法是自定义属性类型,那么任何有关如何处理它的建议都将受到欢迎。我的第一个想法是使用 TextProperty 来存储策略对象 ( policy) 的字符串 rep,在需要时对其进行解码,缓存结果,并对策略对象进行任何更改使缓存无效并更新字符串 rep。

4

2 回答 2

1

尝试将其存储在数据存储中会使您过于复杂。这属于代码而不是数据存储。

最简单的方法是:

class Event(db.Model):
    privacy = db.IntegerProperty()

    def can_see(self, user):
        if self.privacy == PRIVACY_OWNER_ONLY:
            return user == event.owner
        else if self.privacy == PRIVACY_GROUP:
            for grp in self.owner.groups()
                if grp.is_member(user):
                    return True
            return False
于 2012-05-28T22:56:49.737 回答
0

有时只需要考虑正确的方法。解决方案是引入一种使用 pickle 来存储和检索值的新属性,例如https://groups.google.com/forum/?fromgroups#!topic/google-appengine/bwMD0ZfRnJg中描述的属性

我想要一些更复杂的东西,因为泡菜并不总是答案,而且无论如何文档都很好,所以这是我的 ObjectReference 类型:

import pickle
from google.appengine.ext import db

class ObjectProperty(db.Property):
    def __init__(self, object_type=None, verbose_name=None, to_store=pickle.dumps, from_store=pickle.loads, **kwds):
        """Initializes this Property with all the given options

        All args are passed to the superclass. The ones used specifically by this class are described here. For
        all other args, see base class method documentation for details.

        Args:
          object_type: If not None, all values assigned to the property must be either instances of this type or None
          to_store: A function to use to convert a property value to a storable str representation. The default is
            to use pickle.dumps()
          from_store: A function to use to convert a storable str representation to a property value. The default is
            to use pickle.loads()
        """
        if object_type and not isinstance(object_type, type):
            raise TypeError('object_type should be a type object')

        kwds['indexed'] = False         # It never makes sense to index pickled data
        super(ObjectProperty, self).__init__(verbose_name, **kwds)

        self.to_store = to_store
        self.from_store = from_store
        self.object_type = object_type

    def get_value_for_datastore(self, model_instance):
        """Get value from property to send to datastore.

        We retrieve the value of the attribute and return the result of invoking the to_store function on it

        See base class method documentation for details.
        """
        value = getattr(model_instance, self.name, None)
        return self.to_store(value)

    def make_value_from_datastore(self, rep):
        """Get value from datastore to assign to the property.

        We take the value passed, convert it to str() and return the result of invoking the from_store function
        on it. The Property class assigns this returned value to the property.

        See base class method documentation for details.
        """
        # It passes us a unicode, even though I returned a str, so this is required
        rep = str(rep)
        return self.from_store(rep)

    def validate(self, value):
        """Validate reference.

        Returns:
          A valid value.

        Raises:
          BadValueError for the following reasons:
            - Object not of correct type.
        """
        value = super(ObjectProperty, self).validate(value)

        if value is not None and not isinstance(value, self.object_type):
            raise db.KindError('Property %s must be of type %s' % (self.name, self.object_type))

        return value
于 2012-06-01T15:13:17.480 回答