Kuberski - 酷伯司机

写在代码边上
  博客园  :: 首页  :: 联系 :: 订阅 订阅  :: 管理

(GAE文档翻译)Google App Engine中如何修改你的数据模型

Posted on 2008-08-15 14:22  kuber  阅读(2698)  评论(1编辑  收藏  举报
原文: http://code.google.com/appengine/articles/update_schema.html
我发在译言上的中英文对照版: http://www.yeeyan.com/articles/view/41148/12436/dz

更新的数据模型

Mark Ivey, Google Engineer
2008年6月

导言

GAE应用开发中 不可避免的你会需要修改你的数据模型. 本文通过一个小例子介绍了修改数据模型的两个基本步骤:

  1. 更新数据模型类定义
  2. 更新Datastore中的已有数据实体(这一步并不是总是必要的, 下面会讲什么时候你需要这样做)。

开始之前

在更新你的数据模型时,可能需要暂时禁止用户在你的应用中更新数据。 是否确实需要取决于你的应用, 但是在某些情况下, 暂时禁止用户输入会大大便于你更新已有数据。

更新你的数据模型

这里有一个例子,一个简单的图片:

classPicture(db.Model):   
author
= db.UserProperty()  
png_data
= db.BlobProperty()  
name
= db.StringProperty(default='')  # 唯一的
的图片名

我们来修改这个model, 为每个图片加上评分为了保存评分, 我们保存用户评分的次数和评价得分值。更新这个数据模型很容易,我们只是增加两个新的属性:

class Picture(db.Model):
  author
= db.UserProperty()
  png_data
= db.BlobProperty()
  name
= db.StringProperty(default='')  # 唯一的的图片名
 
num_votes = db.IntegerProperty(default=0)
  avg_rating
= db.FloatProperty(default=0)

现在所有保存到Datastore的新建实体将获得一个默认的评分为0 。请注意,Datastore中现有的数据并没有自动被修改, 所以他们不会有这些属性。

更新现有数据实体

App Engine datastore并不要求所有数据有相同的一组属性。 更新的数据模型之后,现有的数据实体将继续没有这些属性。 在某些情况下,这就够了,你不需要做什么。 那什么时候你想更新现有的数据,使他们也有新的属性? 一种情况是当想要对新的属性做查询。在我们这个Picture的例子中, 查询"最受欢迎"或者"最不受欢迎"图片不会返回更新之前的数据, 因为它们没有相应的评分属性要解决此问题,我们需要更新Datastore中现有的数据实体。

从概念上来说,更新现有的数据实体很容易。你只需要创建request handler, 取出每个实体,设置新属性的值, 然后保存数据。 有两个我们必须要解决的问题

  • 查询返回数据集有1000条的上限。如果有多于1000条记录,需要多次查询以获得所有记录。
  • GAE要求http request 在很短的时间内必须返回,否则request 会超时如果有很多数据,request handler不能在一个请求中处理完所有数据
解决方法是在一个request 中只更新一小部分数据。通过使多个request,我们更新所有的数据,而不超过查询上限和request超时限制。为简单起见, 我们在一个request中只更新一条数据,象这样:
  1. 读取一个数据实体
  2. 设置属性的值(如果属性有缺省值,会自动设置)
  3. 保存数据
  4. 用meta refresh标记,让浏览器访问更新下一个数据的URL

一句警告:写读取查询时,应避免使用OFFSET(它不适合大数据集)而是使用WHERE语句量限制返回的数据数量。如果的数据已经有某种唯一值的属性,这很容易。 在这个例子中,图片名称(name)是唯一的,所以我们对name将使用WHERE语句。

代码如下:

# Request handler for the URL /update_datastore
def get(self):
  name
= self.request.get('name',None)
 
if name isNone:
   
# First request, just get the first name out of the datastore.
    pic
= models.Picture.gql('ORDER BY name DESC').get()
    name
= pic.name

  q
= models.Picture.gql('WHERE name <= :1 ORDER BY name DESC', name)
  pics
= q.fetch(limit=2)
  current_pic
= pics[0]
 
if len(pics)==2:
    next_name
= pics[1].name
    next_url
='/update_datastore?name=%s'% urllib.quote(next_name)
 
else:
    next_name
='FINISHED'
    next_url
='/'  # Finished processing, go back to main page.
 
# In this example, the default values of 0 for num_votes and avg_rating are
 
# acceptable, so we don't need to do anything other than call put().

  current_pic
.put()

  context
={
   
'current_name': name,
   
'next_name': next_name,
   
'next_url': next_url,
 
}
  self
.response.out.write(template.render('update_datastore.html', context))

相应的template显示我们正在更新哪条记录, 并用meta refresh 自动转到下条记录


<html>
<head>
 
<metahttp-equiv="refresh"content="0;url={{ next_url }}"/>
</head>
<body>
 
<h3>Update Datastore</h3>
 
<ul>
   
<li>Updated: {{ current_name }}</li>
   
<li>About to update: {{ next_name }}</li>
 
</ul>
</body>
</html>

如果你的数据中没有具有唯一值的属性,上面的例子不能直接使用(当某个属性值对应很多条记录时, 它会很慢) 。需要扩展它以能够处理这种情况。是概念是相同的,使用WHERE限制查询返回的数据集数目以分批更新数据,然后逐条保持。

Datastore中移除已删除的属性

如果你从数据模型中移除了一个属性,你会发现现有的实体仍然有这个属性。它仍然会显示在管理控制台(admin console)中,并仍然存在于Datastore众。 要真正清理旧数据,你需要的遍历每条记录并逐条移除属性值。

  1. 确认你已从数据模型定义删除了属性。
  2. 如果的数据模型类继承自db.model ,它改为继承自db.expando 。(db.model实例无法动态修改,这是我们在下一步要做的) 。
  3. 遍历现有的所有数据(如上文所述) 。 对每条数据,使用delattr删除属性,然后保存数据。
  4. 如果的数据模型原本继承自db.model ,不要忘了改回去。

以后

这种多次request的处理方法现在是可行的。 不过现在我们正在做一些离线处理的解决方法。当这些成为可用的,可能会提供一个更合理的方式来修改你的数据,而且不用加重服务器的负担。你可以考虑订阅我们的blog,跟踪最新的进展

我的GAE文章: http://www.cnblogs.com/kuber/tag/appengine/
我的GAE site: http://feedzshare.appspot.com