Commit a51713a0 authored by Serge S. Koval's avatar Serge S. Koval

Fixed #897 - Ability to disable count queries and have back/fwd pager

parent 14dec4f5
......@@ -484,7 +484,7 @@ class ModelView(BaseModelView):
query = self._search(query, search)
# Get count
count = query.count()
count = query.count() if not self.simple_list_pager else None
# Sorting
if sort_column:
......@@ -592,7 +592,7 @@ class ModelView(BaseModelView):
return False
else:
self.after_model_delete(model)
return True
......
......@@ -339,7 +339,7 @@ class ModelView(BaseModelView):
query = f.apply(query, f.clean(value))
# Get count
count = query.count()
count = query.count() if not self.simple_list_pager else None
# Apply sorting
if sort_column is not None:
......@@ -417,7 +417,7 @@ class ModelView(BaseModelView):
return False
else:
self.after_model_delete(model)
return True
# Default model actions
......
......@@ -222,7 +222,7 @@ class ModelView(BaseModelView):
query = self._search(query, search)
# Get count
count = self.coll.find(query).count()
count = self.coll.find(query).count() if not self.simple_list_pager else None
# Sorting
sort_by = None
......@@ -337,7 +337,7 @@ class ModelView(BaseModelView):
return False
else:
self.after_model_delete(model)
return True
# Default model actions
......
......@@ -4,7 +4,7 @@ import warnings
from sqlalchemy.orm.attributes import InstrumentedAttribute
from sqlalchemy.orm import joinedload
from sqlalchemy.sql.expression import desc
from sqlalchemy import Column, Boolean, func, or_
from sqlalchemy import Boolean, func, or_
from sqlalchemy.exc import IntegrityError
from flask import flash
......@@ -765,7 +765,7 @@ class ModelView(BaseModelView):
joins = set()
query = self.get_query()
count_query = self.get_count_query()
count_query = self.get_count_query() if not self.simple_list_pager else None
# Ignore eager-loaded relations (prevent unnecessary joins)
# TODO: Separate join detection for query and count query?
......@@ -781,7 +781,9 @@ class ModelView(BaseModelView):
for table in self._search_joins:
if table.name not in joins:
query = query.outerjoin(table)
count_query = count_query.outerjoin(table)
if count_query is not None:
count_query = count_query.outerjoin(table)
joins.add(table.name)
......@@ -795,7 +797,9 @@ class ModelView(BaseModelView):
stmt = tools.parse_like_term(term)
filter_stmt = [c.ilike(stmt) for c in self._search_fields]
query = query.filter(or_(*filter_stmt))
count_query = count_query.filter(or_(*filter_stmt))
if count_query is not None:
count_query = count_query.filter(or_(*filter_stmt))
# Apply filters
if filters and self._filters:
......@@ -811,15 +815,20 @@ class ModelView(BaseModelView):
for table in join_tables:
if table.name not in joins:
query = query.join(table)
count_query = count_query.join(table)
if count_query is not None:
count_query = count_query.join(table)
joins.add(table.name)
# turn into python format with .clean() and apply filter
query = flt.apply(query, flt.clean(value))
count_query = flt.apply(count_query, flt.clean(value))
# Calculate number of rows
count = count_query.scalar()
if count_query is not None:
count_query = flt.apply(count_query, flt.clean(value))
# Calculate number of rows if necessary
count = count_query.scalar() if count_query else None
# Auto join
for j in self._auto_joins:
......@@ -944,7 +953,7 @@ class ModelView(BaseModelView):
return False
else:
self.after_model_delete(model)
return True
# Default model actions
......
......@@ -321,6 +321,12 @@ class BaseModelView(BaseView, ActionsMixin):
Controls if the primary key should be displayed in the list view.
"""
simple_list_pager = False
"""
Enable or disable simple list pager.
If enabled, model interface would not run count query and will only show prev/next pager buttons.
"""
form = None
"""
Form class. Override if you want to use custom form for your model.
......@@ -1486,9 +1492,12 @@ class BaseModelView(BaseView, ActionsMixin):
view_args.search, view_args.filters)
# Calculate number of pages
num_pages = count // self.page_size
if count % self.page_size != 0:
num_pages += 1
if count is not None:
num_pages = count // self.page_size
if count % self.page_size != 0:
num_pages += 1
else:
num_pages = None
# Various URL generation helpers
def pager_url(p):
......@@ -1531,6 +1540,7 @@ class BaseModelView(BaseView, ActionsMixin):
pager_url=pager_url,
num_pages=num_pages,
page=view_args.page,
page_size=self.page_size,
# Sorting
sort_column=view_args.sort,
......
......@@ -76,6 +76,31 @@
{% endif %}
{%- endmacro %}
{% macro simple_pager(page, have_next, generator) -%}
<div class="pagination">
<ul>
{% if page > 0 %}
<li>
<a href="{{ generator(page - 1) }}">&lt;</a>
</li>
{% else %}
<li class="disabled">
<a href="{{ generator(0) }}">&lt;</a>
</li>
{% endif %}
{% if have_next %}
<li>
<a href="{{ generator(page + 1) }}">&gt;</a>
</li>
{% else %}
<li class="disabled">
<a href="{{ generator(page) }}">&gt;</a>
</li>
{% endif %}
</ul>
</div>
{%- endmacro %}
{# ---------------------- Forms -------------------------- #}
{% macro render_field(form, field, kwargs={}, caller=None) %}
{% set direct_error = h.is_field_error(field.errors) %}
......
......@@ -13,7 +13,7 @@
{% block model_menu_bar %}
<ul class="nav nav-tabs actions-nav">
<li class="active">
<a href="javascript:void(0)">{{ _gettext('List') }} ({{ count }})</a>
<a href="javascript:void(0)">{{ _gettext('List') }}{% if count %} ({{ count }}){% endif %}</a>
</li>
{% if admin_view.can_create %}
<li>
......@@ -147,7 +147,13 @@
</tr>
{% endfor %}
</table>
{% block list_pager %}
{% if num_pages is not none %}
{{ lib.pager(page, num_pages, pager_url) }}
{% else %}
{{ lib.simple_pager(page, data|length == page_size, pager_url) }}
{% endif %}
{% endblock %}
{% endblock %}
{{ actionlib.form(actions, get_url('.action_view')) }}
......
......@@ -74,6 +74,29 @@
{% endif %}
{%- endmacro %}
{% macro simple_pager(page, have_next, generator) -%}
<ul class="pagination">
{% if page > 0 %}
<li>
<a href="{{ generator(page - 1) }}">&lt;</a>
</li>
{% else %}
<li class="disabled">
<a href="{{ generator(0) }}">&lt;</a>
</li>
{% endif %}
{% if have_next %}
<li>
<a href="{{ generator(page + 1) }}">&gt;</a>
</li>
{% else %}
<li class="disabled">
<a href="{{ generator(page) }}">&gt;</a>
</li>
{% endif %}
</ul>
{%- endmacro %}
{# ---------------------- Forms -------------------------- #}
{% macro render_field(form, field, kwargs={}, caller=None) %}
{% set direct_error = h.is_field_error(field.errors) %}
......
......@@ -13,7 +13,7 @@
{% block model_menu_bar %}
<ul class="nav nav-tabs actions-nav">
<li class="active">
<a href="javascript:void(0)">{{ _gettext('List') }} ({{ count }})</a>
<a href="javascript:void(0)">{{ _gettext('List') }}{% if count %} ({{ count }}){% endif %}</a>
</li>
{% if admin_view.can_create %}
<li>
......@@ -146,7 +146,13 @@
</tr>
{% endfor %}
</table>
{% block list_pager %}
{% if num_pages is not none %}
{{ lib.pager(page, num_pages, pager_url) }}
{% else %}
{{ lib.simple_pager(page, data|length == page_size, pager_url) }}
{% endif %}
{% endblock %}
{% endblock %}
{{ actionlib.form(actions, get_url('.action_view')) }}
......
......@@ -948,3 +948,20 @@ def test_form_args_embeddeddoc():
eq_(form.timestamp.label.text, 'Last Updated Time')
# This is the failure
eq_(form.info.label.text, 'Information')
def test_simple_list_pager():
app, db, admin = setup()
Model1, _ = create_models(db)
class TestModelView(CustomModelView):
simple_list_pager = True
def get_count_query(self):
assert False
view = TestModelView(Model1)
admin.add_view(view)
count, data = view.get_list(0, None, None, None, None)
ok_(count is None)
......@@ -12,6 +12,7 @@ from . import setup
from datetime import datetime, time, date
class CustomModelView(ModelView):
def __init__(self, model, session,
name=None, category=None, endpoint=None, url=None,
......@@ -1680,3 +1681,21 @@ def test_safe_redirect():
assert_true(rv.location.startswith('http://localhost/admin/model1/edit/'))
assert_true('url=%2Fadmin%2Fmodel1%2F' in rv.location)
assert_true('id=2' in rv.location)
def test_simple_list_pager():
app, db, admin = setup()
Model1, _ = create_models(db)
db.create_all()
class TestModelView(CustomModelView):
simple_list_pager = True
def get_count_query(self):
assert False
view = TestModelView(Model1, db.session)
admin.add_view(view)
count, data = view.get_list(0, None, None, None, None)
assert_true(count is None)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment