zoukankan      html  css  js  c++  java
  • Google App Engine:如何修改你的数据模型

    导言

    如果你有一个成功的GAE应用, 不可避免的你会要修改你的数据库架构. 本文通过一个小例子介绍了修改数据库架构的两个基本步骤:

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

    开始之前

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

    更新你的数据模型

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

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

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

    classPicture(db.Model):
      author = db.UserProperty()
      png_data = db.BlobProperty()
      name = db.StringProperty(default='') # Unique name.
      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,跟踪最新的进展

  • 相关阅读:
    Redis学习
    MySQL索引
    细数 Java 线程池的原理
    红黑树学习
    HashMap学习
    Java集合框架
    Java性能优化的45个细节
    MyBatis理解
    jenkins+git+maven+tomcat+jdk本地部署windows版
    windows版docker安装nginx,并设置目录挂载
  • 原文地址:https://www.cnblogs.com/Jayan/p/1387170.html
Copyright © 2011-2022 走看看