光标向后分页正在工作但缺少一个项目

时间:2012-04-20 04:52:47

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

looking for ideas/alternatives to providing a page/item count/navigation of items matching a GAE datastore query,我可以通过REVERSING ORDER找到如何使用单个光标向后翻页导航的提示。

class CursorTests(test_utils.NDBTest):

  def testFirst(self):
    class Bar(model.Model):
      value = model.IntegerProperty()

    self.entities = []
    for i in range(10):
        e = Bar(value=i)
        e.put()
        self.entities.append(e)

    q = Bar.query()
    bars, next_cursor, more = q.order(Bar.key).fetch_page(3)
    barz, another_cursor, more2 = q.order(-Bar.key).fetch_page(3, start_cursor=next_cursor)
    self.assertEqual(len(bars), len(barz))

不幸的是,这个错误失败了。

  

Traceback(最近一次调用最后一次):文件   “/Users/reiot/Documents/Works/appengine-ndb-experiment/ndb/query_test.py”   第32行,在testFirst中       self.assertEqual(len(bars),len(baz))AssertionError:3!= 2

是的,反向查询中缺少边界中的项目。

bars = [Bar(key=Key('Bar', 1), value=0), Bar(key=Key('Bar', 2), value=1), Bar(key=Key('Bar', 3), value=2)] 
bars = [Bar(key=Key('Bar', 2), value=1), Bar(key=Key('Bar', 1), value=0)]

如何解决此问题?

2 个答案:

答案 0 :(得分:14)

好的,这是官方的答案。您需要“反转”光标,如下所示:

rev_cursor = cursor.reversed()

我自己也不知道。 :-(我会确保这在fetch_page()的文档中显示。

答案 1 :(得分:2)

处理这些多个游标,加上正向和反向查询不仅太复杂,而且不允许直接分页(转到第7页),页面底部有一组页面链接,如此“<< ; 1 2 3 4 5>>“,因为您不知道会有多少页。

出于这个原因,我的解决方案是获取整个结果集,或者至少是一个重要的结果集,例如对应于10个页面,然后进行简单的划分来处理页面。为了不浪费Ndb带宽(和成本),您首先要使用keys_only=True获取结果。确定与当前页面对应的集合后,在实体上执行key.get()。如果你想要,你可以考虑在memcache中保存完整的密钥列表几分钟,这样查询就不会重新运行,尽管到目前为止我还没有发现这是必要的。

这是一个示例实现:

def session_list():
    page = request.args.get('page', 0, type=int)

    sessions_keys = Session.query().order(-Session.time_opened).fetch(100, keys_only=True)
    sessions_keys, paging = generic_list_paging(sessions_keys, page)
    sessions = ndb.get_multi(sessions_keys)

    return render_template('generic_list.html', objects=sessions, paging=paging)

它正在使用generic_list_paging函数执行分页分区并在结果集中提取正确的子列表:

def generic_list_paging(objects, page, page_size=10):
    nb_items = len(objects)
    item_start = min(page * page_size, nb_items)
    item_end = min((page + 1) * page_size, nb_items)
    page_max = (nb_items - 1) // page_size + 1
    objects = objects[item_start: item_end]
    paging = {'page': page, 'page_max': page_max}
    return objects, paging

最后,如果你使用的是Jinja2,这里是使用paging字典的分页导航:

{% if paging.page_max > 1 %}
        <nav>
            <ul class="pagination">
                {% if paging.page > 0 %}
                    <li>
                        <a href="{{ request.path }}?page={{ paging.page-1 }} aria-label="Previous">
                            <span aria-hidden="true">&laquo;</span>
                        </a>
                    </li>
                {% endif %}
                {% for page in range(0,paging.page_max) %}
                    <li {% if page==paging.page %}class="disabled"{% endif %}><a href="{{ request.path }}?page={{ page }}">{{ page+1 }}</a></li>
                {% endfor %}
                {% if paging.page < paging.page_max-1 %}
                    <li>
                        <a href="{{ request.path }}?page={{ paging.page+1 }}" aria-label="Next">
                            <span aria-hidden="true">&raquo;</span>
                        </a>
                    </li>
                {% endif %}
            </ul>
        </nav>
{% endif %}