Datastore query without model class

半城伤御伤魂 提交于 2019-12-13 03:21:10

问题


I recently encountered a situation where one might want to run a datastore query which includes a kind, but the class of the corresponding model is not available (e.g. if it's defined in a module that hasn't been imported yet).

I couldn't find any out-of-the-box way to do this using the google.appengine.ext.db package, so I ended up using the google.appengine.api.datastore.Query class from the low-level datastore API.

This worked fine for my needs (my query only needed to count the number of results, without returning any model instances), but I was wondering if anyone knows of a better solution.

Another approach I've tried (which also worked) was subclassing db.GqlQuery to bypass its constructor. This might not be the cleanest solution, but if anyone is interested, here is the code:

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)

(also available as a gist)


回答1:


You could create a temporary class just to do the query. If you use an Expando model, the properties of the class don't need to match what is actually in the datastore.

class KindName(ndb.Expando):
    pass

You could then do:

KindName.query()

If you need to filter on specific properties, then I suspect you'll have to add them to the temporary class.



来源:https://stackoverflow.com/questions/54900142/datastore-query-without-model-class

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!