在App Engine中,db.ReferenceProperty()和ndb.KeyProperty有什么区别?

16

ReferenceProperty在处理两个模块之间的引用方面非常有帮助。例如:

class UserProf(db.Model):
    name = db.StringProperty(required=True)

class Team(db.Model):
    manager_name = db.ReferenceProperty(UserProf, collection_name='teams')
    name = db.StringProperty(required=True)
  • 要获取团队实例的'manager_name',我们可以使用team_ins.manager_name。
  • 要获取特定用户实例管理的'teams',我们使用user_instance.teams并进行迭代。

是不是看起来很简单易懂?

如果要使用NDB进行相同操作,则需要进行修改:

db.ReferenceProperty(UserProf, collection_name='teams') --> ndb.KeyProperty(kind=UserProf)

  • team_ins.manager_name.get()会给您提供经理名称。
  • 要获取特定用户管理的所有团队,我们需要执行以下操作:

    for team in Team.query(Team.manager_name == user_ins.key): 
        print "team  name:", team.name
    

正如您所看到的,在db中处理这些情况看起来比ndb更容易和可读。

  • 为什么在ndb中删除了ReferenceProperty?
  • 即使是db的查询user_instance.teams,也会像在ndb的for循环中所做的那样执行相同的操作。但在ndb中,我们明确地提到使用for循环。
  • 当我们执行user_instance.teams时,背后发生了什么?

预先感谢您的帮助。

2个回答

27

Tim解释得很好。我们发现一种常见的反模式是使用引用属性并逐个加载它们,因为符号“entity.property1.property2”没有清楚地表明第一个点会导致数据库“获取”操作。因此,我们通过强制您编写“entity.property1.get().property2”来使其更明显,并通过简单地说“entity.property1.get_async()”为一堆实体轻松进行批量预取(不需要Nick博客中的复杂解决方案),这将排队一个单独的批量获取操作而不会阻塞结果,当您下一次使用“entity.property1.get().property2”引用这些属性之一时,这不会启动另一个获取操作,而只是等待该批量获取完成(第二次这样做时,批量获取已经完成)。此外,这种方式可以免费集成进程和缓存。


谢谢。ndb与db相同,但它是显式的,用户将知道有get()操作。在这种情况下没有性能改进。我已经阅读了关于ndb异步的内容,但尚未使用过。 - rajpy
3
@Tim和Guido,你们能否提供使用ndb编写的类似http://code.google.com/p/google-app-engine-samples/中的示例应用程序?还有在基于ndb的应用程序中要遵循的最佳实践。这将非常有帮助。谢谢。 - rajpy
调用 entity.keyproperty.get().somevalue1 将首次获取由 keyproperty 引用的实体,在随后的 entity.keyproperty.get().somevalue2entity.keyproperty.get().somevalue3 中将不会进行数据库调用吗? - Vinod Kumar
好的...既然上下文相同(在同一请求中,如果没有初始化新线程),则当第一次调用get函数时,实体将保存在上下文缓存中,并且在后续对get函数的调用中将返回相同的实体,因此不会进行新的数据库调用。请参阅此链接(https://cloud.google.com/appengine/docs/python/ndb/cache#context)以了解有关上下文缓存的更多信息。 - Vinod Kumar

7

我不知道为什么Guido没有实现引用属性的答案。

然而,我发现使用pre_fetch_refprops http://blog.notdot.net/2010/01/ReferenceProperty-prefetching-in-App-Engine(通过使用get_value_for_datastore获取所有键来预取所有引用属性,然后对键执行get_multi)花费了很多时间。

这样效率大大提高。

另外,如果所引用的对象不存在,则在尝试获取对象时会出错。

如果您将具有引用的对象进行了pickle,则最终pickle了比您计划的要多得多的内容。

因此,我发现除了一个情况外,在该情况下,您拥有单个实体,并且希望使用.name类型访问器获取引用对象,您必须跳过各种困难以防止获取引用实体。


谢谢。希望Guido本人能回答这个问题!:) - rajpy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接