没有模型的数据存储区查询 class
Datastore query without model class
我最近遇到一种情况,有人可能想 运行 包含一种数据存储查询,但是相应模型的 class 不可用(例如,如果它在模块中定义尚未导入)。
我找不到任何开箱即用的方法来使用 google.appengine.ext.db
package, so I ended up using the google.appengine.api.datastore.Query
class from the low-level datastore
API 执行此操作。
这很好地满足了我的需要(我的查询只需要计算结果的数量,而不返回任何模型实例),但我想知道是否有人知道更好的解决方案。
我尝试过的另一种方法(也有效)是 subclassing db.GqlQuery
绕过其构造函数。这可能不是最干净的解决方案,但如果有人感兴趣,这里是代码:
import logging
from google.appengine.ext import db, gql
class ClasslessGqlQuery(db.GqlQuery):
"""
This subclass of :class:`db.GqlQuery` uses a modified version of ``db.GqlQuery``'s constructor to suppress any
:class:`db.KindError` that might be raised by ``db.class_for_kind(kindName)``.
This allows using the functionality :class:`db.GqlQuery` without requiring that a Model class for the query's kind
be available in the local environment, which could happen if a module defining that class hasn't been imported yet.
In that case, no validation of the Model's properties will be performed (will not check whether they're not indexed),
but otherwise, this class should work the same as :class:`db.GqlQuery`.
"""
def __init__(self, query_string, *args, **kwds):
"""
**NOTE**: this is a modified version of :class:`db.GqlQuery`'s constructor, suppressing any :class:`db.KindError`s
that might be raised by ``db.class_for_kind(kindName)``.
In that case, no validation of the Model's properties will be performed (will not check whether they're not indexed),
but otherwise, this class should work the same as :class:`db.GqlQuery`.
Args:
query_string: Properly formatted GQL query string.
*args: Positional arguments used to bind numeric references in the query.
**kwds: Dictionary-based arguments for named references.
Raises:
PropertyError if the query filters or sorts on a property that's not indexed.
"""
from google.appengine.ext import gql
app = kwds.pop('_app', None)
namespace = None
if isinstance(app, tuple):
if len(app) != 2:
raise db.BadArgumentError('_app must have 2 values if type is tuple.')
app, namespace = app
self._proto_query = gql.GQL(query_string, _app=app, namespace=namespace)
kind = self._proto_query._kind
model_class = None
try:
if kind is not None:
model_class = db.class_for_kind(kind)
except db.KindError, e:
logging.warning("%s on %s without a model class", self.__class__.__name__, kind, exc_info=True)
super(db.GqlQuery, self).__init__(model_class)
if model_class is not None:
for property, unused in (self._proto_query.filters().keys() +
self._proto_query.orderings()):
if property in model_class._unindexed_properties:
raise db.PropertyError('Property \'%s\' is not indexed' % property)
self.bind(*args, **kwds)
您可以创建一个临时的 class 来执行查询。如果您使用 Expando 模型,class 的属性不需要与数据存储中的实际内容相匹配。
class KindName(ndb.Expando):
pass
然后你可以这样做:
KindName.query()
如果您需要过滤特定属性,那么我怀疑您必须将它们添加到临时 class。
我最近遇到一种情况,有人可能想 运行 包含一种数据存储查询,但是相应模型的 class 不可用(例如,如果它在模块中定义尚未导入)。
我找不到任何开箱即用的方法来使用 google.appengine.ext.db
package, so I ended up using the google.appengine.api.datastore.Query
class from the low-level datastore
API 执行此操作。
这很好地满足了我的需要(我的查询只需要计算结果的数量,而不返回任何模型实例),但我想知道是否有人知道更好的解决方案。
我尝试过的另一种方法(也有效)是 subclassing db.GqlQuery
绕过其构造函数。这可能不是最干净的解决方案,但如果有人感兴趣,这里是代码:
import logging
from google.appengine.ext import db, gql
class ClasslessGqlQuery(db.GqlQuery):
"""
This subclass of :class:`db.GqlQuery` uses a modified version of ``db.GqlQuery``'s constructor to suppress any
:class:`db.KindError` that might be raised by ``db.class_for_kind(kindName)``.
This allows using the functionality :class:`db.GqlQuery` without requiring that a Model class for the query's kind
be available in the local environment, which could happen if a module defining that class hasn't been imported yet.
In that case, no validation of the Model's properties will be performed (will not check whether they're not indexed),
but otherwise, this class should work the same as :class:`db.GqlQuery`.
"""
def __init__(self, query_string, *args, **kwds):
"""
**NOTE**: this is a modified version of :class:`db.GqlQuery`'s constructor, suppressing any :class:`db.KindError`s
that might be raised by ``db.class_for_kind(kindName)``.
In that case, no validation of the Model's properties will be performed (will not check whether they're not indexed),
but otherwise, this class should work the same as :class:`db.GqlQuery`.
Args:
query_string: Properly formatted GQL query string.
*args: Positional arguments used to bind numeric references in the query.
**kwds: Dictionary-based arguments for named references.
Raises:
PropertyError if the query filters or sorts on a property that's not indexed.
"""
from google.appengine.ext import gql
app = kwds.pop('_app', None)
namespace = None
if isinstance(app, tuple):
if len(app) != 2:
raise db.BadArgumentError('_app must have 2 values if type is tuple.')
app, namespace = app
self._proto_query = gql.GQL(query_string, _app=app, namespace=namespace)
kind = self._proto_query._kind
model_class = None
try:
if kind is not None:
model_class = db.class_for_kind(kind)
except db.KindError, e:
logging.warning("%s on %s without a model class", self.__class__.__name__, kind, exc_info=True)
super(db.GqlQuery, self).__init__(model_class)
if model_class is not None:
for property, unused in (self._proto_query.filters().keys() +
self._proto_query.orderings()):
if property in model_class._unindexed_properties:
raise db.PropertyError('Property \'%s\' is not indexed' % property)
self.bind(*args, **kwds)
您可以创建一个临时的 class 来执行查询。如果您使用 Expando 模型,class 的属性不需要与数据存储中的实际内容相匹配。
class KindName(ndb.Expando):
pass
然后你可以这样做:
KindName.query()
如果您需要过滤特定属性,那么我怀疑您必须将它们添加到临时 class。