建立在布兰登的回答here。
如果可以的话,在 Python 文件中定义所有成就会快得多,因此它们是在内存中的,不需要获取数据库。这也更容易实现。
class StaticAchievement(object):
"""
An achievement defined in a Python file.
"""
by_name = {}
by_index = []
def __init__(self, name, description="", picture=None):
if picture is None: picture = "static/default_achievement.png"
StaticAchievement.by_name[name] = self
StaticAchievement.by_index.append(self)
self.index = len(StaticAchievement.by_index)
# This automatically adds an entry to the StaticAchievement.by_name dict.
# It also adds an entry to to the StaticAchievement.by_index list.
StaticAchievement(
name="tied your shoe",
description="You successfully tied your shoes!",
picture="static/shoes.png"
)
然后您所要做的就是将每个玩家的成就的 id 保存在 db.StringListProperty 中。当你加载了玩家的对象后,渲染成就不需要额外的数据库查找——你已经有了 ID,现在你只需要在 StaticAchievement.all 中查找它们。这很容易实现,并且允许您轻松查询哪些用户具有给定的成就等。不需要其他任何东西。
如果您想要与用户拥有成就相关的其他数据(例如获得成就的日期),那么您可以选择以下方法:
1:将其存储在另一个相同长度的ListProperty中。
这保留了实现的简单性和属性的可索引性。但是,这种解决方案并不符合每个人的口味。如果您需要减少对这些数据的使用混乱,只需在播放器对象上编写一个方法,如下所示:
def achievement_tuples(self):
r = []
for i in range(0, len(self.achievements)):
r.append( (self.achievements[i], self.achievement_dates[i]) )
return r
您可以通过维护整数的并行 ListProperty 来处理进度,并在用户取得进展时递增这些整数。
只要您能够理解数据的表示方式,您就可以轻松地将该表示隐藏在您想要的任何方法后面 - 让您拥有所需的界面以及所需的性能和索引特性。但是,如果您真的不需要索引并且不喜欢列表,请参阅选项 #2。
2:将附加数据存储在 BlobProperty 中。
这需要您对数据进行序列化和反序列化,并且您放弃了对列表属性的漂亮查询,但是如果您讨厌并行列表的概念,也许您会更开心。当人们真正想要 Python 的做事方式时,这就是他们倾向于做的事情,这与 App Engine 的方式不同。
3:将附加数据存储在数据库中
(例如,一个包含成就 id 的 StringProperty、一个 DateProperty 和一个 UserProperty 的 PlayersAchievement 对象;在玩家上,一个充满了对 PlayersAchievement 对象的引用的成就列表属性)
由于我们希望玩家有可能获得大量成就,因此开销会很快变差。绕过它很快就会变得非常复杂:memcache,存储中间数据,如预渲染 HTML 的 blob 或序列化的元组列表,设置任务等。如果你想要成就定义,这也是你必须做的事情自己可以修改/存储在数据库中。