如何处理属于Model对象状态的对象,但不需要单独的数据库级支持?

时间:2012-05-28 22:36:28

标签: google-app-engine

在我的Google App Engine应用中,我有需要存储的模型对象。这些对象由各种策略对象参数化。例如,我的Event类有一个Privacy策略对象,用于确定谁可以查看,更新等.VealthPolicy的各种子类的行为有所不同。该活动在各个点咨询其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类使用ReferenceProperty来访问PrivacyPolicy:

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

我不喜欢这个的原因是一对一的关系意味着没有人每次都对策略对象进行查询,也没有必要保持从策略到其Event对象的反向引用,并且没有其他方法是PrivacyPolicy独立的数据库级对象。它在功能上等同于IntegerProperty,因为它是Event对象状态的一部分,它只是一个对象而不是一个数字 - 特别是它是一个可以具有零状态或大量状态的对象,对于Event类型是未知的。

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

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

2 个答案:

答案 0 :(得分: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

答案 1 :(得分:0)

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

我想要一些稍微复杂的东西,因为pickle并不总是答案,无论如何文档很好,所以这是我的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