2

在阅读了 GAE Datastore API 之后,我仍然不确定是否需要将键名和父级复制为实体的属性。

假设有两种实体:Employee 和 Division。每个员工都有一个部门作为其父级,并由一个帐户名称标识。我使用帐户名作为员工的键名。但是在为 Employee 建模时,我仍然会将这两个保留为属性:

division = db.ReferenceProperty(Division)
account_name = db.StringProperty()

显然,我必须手动division与其父级保持一致,并account_name与其键名保持一致。我做这项额外工作的原因是:

  1. 恐怕 GQL/Datastore API 可能不支持父级和键名以及普通属性。我可以对属性做些什么,而不是父名或键名(或者它们本质上是引用属性)?如何在 GQL 查询中使用键名?
  2. 键名和父级的含义不是特别清楚。由于名称不是自我描述的,我必须通知其他贡献者我们使用帐户名称作为键名......

但这确实是不必要的工作,浪费时间和存储空间。我无法摆脱 SQL 的想法——为什么谷歌不让我们定义一个属性作为关键?另一个是父母?然后我们可以命名它们并用作普通属性......

这里的最佳做法是什么?

4

2 回答 2

5

请记住,在 GAE 数据存储中,一旦创建实体,您将永远无法更改实体的 parent 或 key_name。这些值在实体的生命周期内是永久的。

如果 Employee 的 account_name 有很小的可能发生变化,那么您就不能将其用作 key_name。如果它永远不会改变,那么它可能是一个非常好的 key_name,并且允许您使用 Employee.get_by_key_name() 而不是昂贵的查询为员工进行廉价获取。

Parent 并不等同于外键。外键更好的等价物是引用属性。

使用 parent 的主要原因是父实体和子实体位于同一个实体组中,这允许您在单个事务中对它们进行操作。如果您只需要从 Employee 中引用部门,则只需使用引用属性。我建议熟悉实体组的工作方式,因为这对 GAE 数据建模非常重要:

使用 parent 也可能导致写入性能问题,因为写入单个实体组的速度有限制(大约每秒一次写入)。在决定是使用父属性还是引用属性时,您需要考虑在同一事务中需要修改哪些实体。在许多情况下,您可以改用跨组 (XG) 事务。这完全取决于您想要做出哪些权衡。

所以我的建议是:

  • 如果您的员工 account_name 绝对不会更改,则将其用作 key_name。否则,只需将其设为基本属性。
  • 如果您需要在同一事务中修改 Employee 和 Division(并且您不能让它与 XG 事务一起使用)并且您永远不会更改 Employee 的 Division,那么将 Division 设置为 Employee 的父级。否则,只需使用参考属性对这种关系进行建模。
于 2012-09-07T19:33:36.690 回答
3

当您创建一个以 Divison 为父对象的新 Employee 对象时,它将如下所示:

div = Division()
... #Complete the division properties
div.put()
emp = Employee(key_name=<account_name>, parent=div)
... #Complete the employee properties
emp.put()

然后,当您想获得对 Employee 所属部门的引用时:

div = emp.parent()
#Get the Employee account_name (which is the employees's key name):
account_name = emp.key().name()

您不必将 RefrenceProperty 存储到员工所属的部门,因为它已经在父级中完成。此外,您可以根据需要从 Employee 实体的密钥中获取 account_name。

要查询密钥:

emp = Employee.get_by_key_name(<account_name>, parent=<division>)
#OR 
div = Division.get_by_key_name(<keyname>)

#Get all employees in a division
emps = Employee.all().ancestor(div)
于 2012-09-07T16:29:43.780 回答