在重复的StructuredProperty中,Expando模型未正确保存自定义属性

时间:2014-03-04 23:50:26

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

我正在尝试将Expando模型用作另一个模型中的重复StructuredProperty。也就是说,我想向我的Accounts模型添加无限数量的User。由于Accounts可以根据其类型具有不同的属性(Accounts是对社交网络帐户的引用,例如Twitter在OAuth流程中需要比Facebook更多的信息),我设计了{{1} }模型为Account。我已在模型定义中添加了所有基本信息,但我计划为特定社交网络添加自定义属性(例如,Twitter的特定Expando属性)。

1 /您能否确认以下设计(access_token_secret

Expando

2 /现在我遇到的问题是:当我向我的StructuredProperty实例添加Facebook帐户时,一切正常;但是,当我将Twitter帐户附加到同一个实例时,问题就会增加,并添加一个未在模型中声明的新属性,如:

class Account(ndb.Expando):
    account_type = ndb.StringProperty(required=True, choices=['fb', 'tw', 'li'])
    account_id = ndb.StringProperty()
    state = ndb.StringProperty()
    access_token = ndb.StringProperty()

class HUser(User):
    email = ndb.StringProperty(required=True, validator=validate_email)
    created = ndb.DateTimeProperty(auto_now_add=True)
    accounts = ndb.StructuredProperty(Account, repeated=True)

此操作应该将访问令牌密钥保存在HUser的Twitter for account in huser.accounts: if account.state == "state_we_re_looking_for" and account.account_type == 'tw': # we found the appropriate Twitter account reference account.access_token_secret = "..." # store the access token secret fetched from Twitter API huser.put() # save to the Datastore break 实例中,但事实上它将其保存在 Facebook {{1}中实例(在索引0处)!

我做错了什么?

感谢。

2 个答案:

答案 0 :(得分:1)

根据我的理解,App Engine NDB似乎不支持包含Expando个实体的Expando个实体。

我最初没有意识到的一件事是我的HUser模型继承自Google的User类,这正是Expando模型!

所以我甚至不知道它,我试图将StructuredProperty ExpandoExpando个对象放在另一个Account内,似乎不支持(I然而,没有找到任何关于这个限制的明确写法。

解决方案是以不同的方式设计数据模型。我将Expando个对象放在一个单独的实体类中(这次,它们是真正的KeyProperty个对象!),我添加了HUser来引用{{1}}实体。这涉及更多的读/写操作,但代码实际上更容易阅读...

我会将自己的问题标记为已回答,除非有人对此处发现的限制有另一个有趣的意见。

答案 1 :(得分:1)

这是ndb如何存储StructuredProperty的基本问题。数据存储目前没有办法存储它,所以ndb基本上会破坏你的属性。

例如,考虑实体:

HUser(email='test@example.com'.
      accounts=(Account(type='fb',
                        account_id='1',
                        state='1',
                        access_token='1'),
                Account(type='tw',
                        account_id='2',
                        state='2',
                        access_token='2',
                        access_token_secret='2')))

这实际上会存储在一个看起来像这样的实体中:

{
 email : 'test@example.com',
 accounts.type : ['fb', 'tw'],
 accounts.account_id : ['1', '2'],
 accounts.state : ['1', '2'],
 accounts.access_token : ['1', '2'],
 accounts.access_token_secret : ['2']
}

由于您使用的是ndb.Expando,因此ndb不知道它应该使用access_token_secret填充None字段用于Facebook帐户。当ndb重新填充您的实体时,它会为其看到的第一个帐户填写access_token_secret,即facebook帐户。

重新构建数据听起来像是正确的方法,但您可能希望让您的HUser成为Account的{​​{1}}的祖先,以便您使用{查询用户的帐户{3}}