如何通过密钥订购NDB查询?

时间:2017-03-09 10:05:44

标签: python-2.7 google-app-engine app-engine-ndb

我尝试在Google App Engine上使用任务队列。我想利用App Engine文档中显示的Mapper类" Background work with the deferred library"。 我通过密钥

对查询结果的排序有一个例外
def get_query(self):
    ...
    q = q.order("__key__")
    ...

例外:

File "C:... mapper.py", line 41, in get_query
    q = q.order("__key__")
  File "C:\Program Files (x86)\Google\google_appengine\google\appengine\ext\ndb\query.py", line 1124, in order
    'received %r' % arg)
TypeError: order() expects a Property or query Order; received '__key__'
INFO     2017-03-09 11:56:32,448 module.py:806] default: "POST /_ah/queue/deferred HTTP/1.1" 500 114

这篇文章是从2009年开始的,所以我想有些事情可能已经改变了。 我的环境:Windows 7,Python 2.7.9,Google App Engine SDK 1.9.50

关于SO的NDB订购有somewhat similar questions。 这个代码来自官方文档的错误,大概是2017年2月(最近)更新的,并且由声誉的SO用户排名前0.1%。

所以我一定做错了。解决方案是什么?

1 个答案:

答案 0 :(得分:1)

宾果。 Avinash Raj是对的。如果这是一个答案,我会接受它。 这是完整的类代码

#!/usr/bin/python2.7
# -*- coding: utf-8 -*-
from google.appengine.ext import deferred
from google.appengine.ext import ndb
from google.appengine.runtime import DeadlineExceededError
import logging

class Mapper(object):
    """
    from https://cloud.google.com/appengine/docs/standard/python/ndb/queries
    corrected with suggestions from Stack Overflow
    http://stackoverflow.com/questions/42692319/how-to-order-ndb-query-by-the-key
    """
    # Subclasses should replace this with a model class (eg, model.Person).
    KIND = None

    # Subclasses can replace this with a list of (property, value) tuples to filter by.
    FILTERS = []

    def __init__(self):
        logging.info("Mapper.__init__: {}")
        self.to_put = []
        self.to_delete = []

    def map(self, entity):
        """Updates a single entity.
        Implementers should return a tuple containing two iterables (to_update, to_delete).
        """
        return ([], [])

    def finish(self):
        """Called when the mapper has finished, to allow for any final work to be done."""
        pass

    def get_query(self):
        """Returns a query over the specified kind, with any appropriate filters applied."""
        q = self.KIND.query()
        for prop, value in self.FILTERS:
            q = q.filter(prop == value)
        if __name__ == '__main__':
            q = q.order(self.KIND.key) # the fixed version. The original q.order('__key__') failed
            # see http://stackoverflow.com/questions/42692319/how-to-order-ndb-query-by-the-key
        return q

    def run(self, batch_size=100):
        """Starts the mapper running."""
        logging.info("Mapper.run: batch_size: {}".format(batch_size))
        self._continue(None, batch_size)

    def _batch_write(self):
        """Writes updates and deletes entities in a batch."""
        if self.to_put:
            ndb.put_multi(self.to_put)
            self.to_put = []
        if self.to_delete:
            ndb.delete_multi(self.to_delete)
            self.to_delete = []

    def _continue(self, start_key, batch_size):
        q = self.get_query()
        # If we're resuming, pick up where we left off last time.
        if start_key:
            key_prop = getattr(self.KIND, '_key')
            q = q.filter(key_prop > start_key)
        # Keep updating records until we run out of time.
        try:
            # Steps over the results, returning each entity and its index.
            for i, entity in enumerate(q):
                map_updates, map_deletes = self.map(entity)
                self.to_put.extend(map_updates)
                self.to_delete.extend(map_deletes)
                # Do updates and deletes in batches.
                if (i + 1) % batch_size == 0:
                    self._batch_write()
                # Record the last entity we processed.
                start_key = entity.key
            self._batch_write()
        except DeadlineExceededError:
            # Write any unfinished updates to the datastore.
            self._batch_write()
            # Queue a new task to pick up where we left off.
            deferred.defer(self._continue, start_key, batch_size)
            return
        self.finish()