从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)]
如何解决此问题?
答案 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">«</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">»</span>
</a>
</li>
{% endif %}
</ul>
</nav>
{% endif %}