我试图了解命名空间和祖先在GAE中是如何工作的。 所以我做了一个简单的多租户" ToDo List"每个租户都是用户的应用程序。所以我在 appengine_config.py 中设置名称空间,如下所示:
def namespace_manager_default_namespace_for_request():
name = users.GetCurrentUser().user_id()
return name
然后在我的 main.py 中我管理了这样的观点:
#Make tasks with strong consistency by giving them a parent
DEFAULT_TASKS_ANCESTOR = ndb.Key('Agenda', 'default_agenda')
class TasksList(webapp2.RequestHandler):
def get(self):
namespace = namespace_manager.get_namespace()
tasks_query = Task.query(ancestor=DEFAULT_TASKS_ANCESTOR)
tasks = tasks_query.fetch(10, use_cache=False)
context = {
'tasks': tasks,
'namespace': namespace,
}
template = JINJA_ENVIRONMENT.get_template('templates/tasks_list.html')
self.response.write(template.render(context))
def post(self):
name = cgi.escape(self.request.get('name'))
task = Task(parent=DEFAULT_TASKS_ANCESTOR)
task.name = name
task.put()
self.redirect('/tasks_list')
......这给出了泄漏的数据:
我以userA身份登录,创建了一个任务,然后注销并以另一个用户(userB)再次登录,我可以从userA看到该任务。 我从管理面板确认任务实体来自不同的命名空间,并且祖先也是不同的"因为它包含命名空间?"。
我还使用.fetch(10, use_cache=False)
关闭了缓存。但问题仍然存在。所以这不是一个缓存问题。
所以最后我在.query()
和.put()
之前创建了父级,它确实有效!像这样:
DEFAULT_TASKS_ANCESTOR = ndb.Key('Agenda', 'default_agenda')
tasks_query = Task.query(ancestor=DEFAULT_TASKS_ANCESTOR)
但现在我有疑问......
为什么会有效?由于这些任务实体不共享相同的命名空间,我如何查询来自另一个命名空间的数据?即使我错误地从另一个命名空间查询祖先,我应该获取数据吗?
来自 namespaceA 的 DEFAULT_TASKS_ANCESTOR 与来自 namespaceB 的 DEFAULT_TASKS_ANCESTOR 相同的祖先,还是他们两个完全不同的祖先?
命名空间是否真的划分了数据存储区中的数据,还是不是这样?
如果祖先即使在命名空间中扮演了重要的角色,我是否仍然应该使用命名空间或祖先来划分多租户应用程序中的数据?
提前谢谢!
答案 0 :(得分:2)
我认为您的问题是在请求之外定义DEFAULT_TASKS_ANCESTOR
常量。在这种情况下,数据存储区使用默认命名空间来创建此密钥。因此,通过查询该祖先密钥,您使用了相同的命名空间。
来自Google的数据存储区documentation:
默认情况下,数据存储区使用命名空间管理器中的当前命名空间设置来获取数据存储区请求。 API在创建时将此当前命名空间应用于Key或Query对象。因此,如果应用程序以序列化形式存储Key或Query对象,则需要小心,因为命名空间在这些序列化中保留。
正如您所知,当您在请求中定义祖先密钥时,它会起作用。您可以在构造函数调用中设置特定查询的命名空间(即ndb.Query(namespace='1234')
),这样您就可以从不同的命名空间中查询实体。
对于您的其他问题: