【问题标题】:Transferring data from product datastore to local development environment datastore in Google App Engine (Python)在 Google App Engine (Python) 中将数据从产品数据存储传输到本地开发环境数据存储
【发布时间】:2018-10-13 00:55:46
【问题描述】:

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

详细问题:

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

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

【问题讨论】:

    标签: python google-app-engine


    【解决方案1】:

    听起来你应该使用远程沙盒

    即使你让它工作,本地数据存储的行为仍然与实际数据存储不同。

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

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

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

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

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

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

    编辑 - 这是我的 to-json/from-json 代码的样子:

    我的所有ndb.Models 都是我的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 dicts 等等):

    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
    

    【讨论】:

    • 谢谢。 “将生产数据克隆到本地主机”可以解决我的问题。我已经将数据存储中的导出脚本编写为 JSON。在本地开发环境中,我开始编写导入脚本,但我做不到。您能否详细说明您的 JSON REST 处理程序逻辑?我需要了解的是;我应该为每个模型重写我的转换代码还是有更简单的方法?
    • 我在上面添加了我的代码。我所有的模型都是BaseModel 的子类,所以这对函数几乎适用于所有模型。在某些模型上,我必须覆盖 to_dtoset_from_dto 以对 BaseModels 功能进行一些调整
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-03
    • 2016-09-01
    • 2016-01-18
    • 1970-01-01
    • 2011-05-26
    • 2016-07-22
    相关资源
    最近更新 更多