将数据从产品数据存储区传输到Google App Engine(Python)中的本地开发环境数据存储区

时间:2018-10-11 08:58:22

标签: python google-app-engine

TL; DR我需要找到一个真正的解决方案,以便从产品数据存储区下载数据并将其加载到本地开发环境中。

详细问题:

我需要使用产品服务器数据存储区中的真实数据(而非实时数据)在本地开发服务器中测试我的应用程序。文档和其他资源提供了三种选择:

  1. 使用appfg.py从产品服务器下载数据,然后将其加载到本地开发环境中。当我使用此方法时,由于Oauth问题,我收到“错误请求”错误。此外,不建议使用此方法。官方文档建议使用第二种方法:
  2. 使用gcloud via managed export and the import。此方法的史诗文档说明了我们如何在控制台上备份所有数据(在https://console.cloud.google.com/中)。我已经尝试过这种方法。备份数据正在云中的存储上生成。我下载了。它是LevelDB格式。我需要将其加载到本地开发服务器中。没有官方解释。第一种方法的加载方法与LevelDB格式不兼容。我找不到解决此问题的正式方法。有一个StackOverflow entry,但它对我不起作用,因为它只获取所有实体作为字典。到“ ndb”实体的“ dic”对象的对话成为棘手的问题。
  3. 我对前两种方法失去了希望,然后决定使用Cloud Datastore Emulator (beta),它可以在本地开发环境中提供模拟真实数据的功能。它仍然是beta版本,并存在一些问题。无论如何,当我运行命令时,都会遇到DATASTORE_EMULATOR_HOST问题。

1 个答案:

答案 0 :(得分:3)

听起来您应该使用远程沙箱

即使您可以使用它,本地主机数据存储的行为仍然与实际数据存储不同。

如果您想真正模拟您的生产环境,那么我建议您将您的App Engine项目的克隆设置为远程沙箱。您可以将应用程序部署到新的gae项目ID appcfg.py update . -A sandbox-id,然后使用数据存储区管理员在Google云存储中创建生产备份,然后在沙箱中使用数据存储区管理员在沙盒中还原此备份。

将生产数据克隆到本地主机

我确实使用一些生产数据来填充我的localhost数据存储,但这不是完整的克隆。只是核心所需的对象和一些测试用户。

为此,我编写了一个google dataflow作业,该作业导出选定的模型并将其以jsonl格式保存在Google云存储中。然后在我的本地主机上,我有一个名为/init/的终结点,它启动任务队列作业以下载这些导出并导入它们。

为此,我重用了我的JSON REST处理程序代码,该代码能够将任何模型转换为json,反之亦然。

理论上,您可以对整个数据存储区执行此操作。

编辑-这是我的to-json / from-json代码如下:

我的ndb.Model的所有子类都是BaseModel的子类,该子类具有通用的转换代码:

get_dto_typemap = {
    ndb.DateTimeProperty: dt_to_timestamp,
    ndb.KeyProperty: key_to_dto,
    ndb.StringProperty: str_to_dto,
    ndb.EnumProperty: str,
}
set_from_dto_typemap = {
    ndb.DateTimeProperty: timestamp_to_dt,
    ndb.KeyProperty: dto_to_key,
    ndb.FloatProperty: float_from_dto,
    ndb.StringProperty: strip,
    ndb.BlobProperty: str,
    ndb.IntegerProperty: int,
}

class BaseModel(ndb.Model):

    def to_dto(self):
        dto = {'key': key_to_dto(self.key)}
        for name, obj in self._properties.iteritems():
            key = obj._name
            value = getattr(self, obj._name)
            if obj.__class__ in get_dto_typemap:
                if obj._repeated:
                    value = [get_dto_typemap[obj.__class__](v) for v in value]
                else:
                    value = get_dto_typemap[obj.__class__](value)
            dto[key] = value
        return dto

    def set_from_dto(self, dto):
        for name, obj in self._properties.iteritems():
            if isinstance(obj, ndb.ComputedProperty):
                continue
            key = obj._name
            if key in dto:
                value = dto[key]
                if not obj._repeated and obj.__class__ in set_from_dto_typemap:
                    try:
                        value = set_from_dto_typemap[obj.__class__](value)
                    except Exception as e:
                        raise Exception("Error setting "+self.__class__.__name__+"."+str(key)+" to '"+str(value) + "': " + e.message)
                try:
                    setattr(self, obj._name, value)
                except Exception as e:
                    print dir(obj)
                    raise Exception("Error setting "+self.__class__.__name__+"."+str(key)+" to '"+str(value)+"': "+e.message)

class User(BaseModel):
    # user fields, etc

我的请求处理程序然后像这样使用set_from_dtoto_dtoBaseHandler还提供了一些方便的方法,可将json负载转换为python dict,而不是其他方式):

class RestHandler(BaseHandler):
    MODEL = None

    def put(self, resource_id=None):
        if resource_id:
            obj = ndb.Key(self.MODEL, urlsafe=resource_id).get()
            if obj:
                obj.set_from_dto(self.json_body)
                obj.put()
                return obj.to_dto()
            else:
                self.abort(422, "Unknown id")
        else:
            self.abort(405)

    def post(self, resource_id=None):
        if resource_id:
            self.abort(405)
        else:
            obj = self.MODEL()
            obj.set_from_dto(self.json_body)
            obj.put()
            return obj.to_dto()

    def get(self, resource_id=None):
        if resource_id:
            obj = ndb.Key(self.MODEL, urlsafe=resource_id).get()
            if obj:
                return obj.to_dto()
            else:
                self.abort(422, "Unknown id")
        else:
            cursor_key = self.request.GET.pop('$cursor', None)
            limit = max(min(200, self.request.GET.pop('$limit', 200)), 10)
            qs = self.MODEL.query()
            # ... other code that handles query params
            results, next_cursor, more = qs.fetch_page(limit, start_cursor=cursor)
            return {
                '$cursor': next_cursor.urlsafe() if more else None,
                'results': [result.to_dto() for result in results],
            }

class UserHandler(RestHandler):
    MODEL = User