In Python's Flask-Admin for database table viewing/administrating, I need the view to open automatically to the last page of the paginated data.
Important: I cannot simply sort the records descending so the last record shows first.
Here's what my simple example below looks like. I'd like it to start on the last page, as pictured.
Here's some example code to reproduce my model:
import os
import os.path as op
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
import flask_admin as admin
from flask_admin.contrib.sqla import ModelView
# Create application
app = Flask(__name__)
# Create dummy secrey key so we can use sessions
app.config['SECRET_KEY'] = '123456790'
# Create in-memory database
app.config['DATABASE_FILE'] = 'sample_db2.sqlite'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + app.config['DATABASE_FILE']
app.config['SQLALCHEMY_ECHO'] = True
db = SQLAlchemy(app)
# Models
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.Unicode(64))
email = db.Column(db.Unicode(64))
def __unicode__(self):
return self.name
class UserAdmin(ModelView):
column_searchable_list = ('name', 'email')
column_editable_list = ('name', 'email')
column_display_pk = True
can_set_page_size = True
# the number of entries to display in the list view
page_size = 5
# Flask views
@app.route('/')
def index():
return '<a href="/admin/">Click me to get to Admin!</a>'
admin = admin.Admin(app, 'Example', template_mode='bootstrap3')
# Add views
admin.add_view(UserAdmin(User, db.session))
def build_sample_db():
"""
Populate a small db with some example entries.
"""
db.drop_all()
db.create_all()
first_names = [
'Harry', 'Amelia', 'Oliver', 'Jack', 'Isabella', 'Charlie','Sophie', 'Mia',
'Jacob', 'Thomas', 'Emily', 'Lily', 'Ava', 'Isla', 'Alfie', 'Olivia', 'Jessica',
'Riley', 'William', 'James', 'Geoffrey', 'Lisa', 'Benjamin', 'Stacey', 'Lucy'
]
last_names = [
'Brown', 'Smith', 'Patel', 'Jones', 'Williams', 'Johnson', 'Taylor', 'Thomas',
'Roberts', 'Khan', 'Lewis', 'Jackson', 'Clarke', 'James', 'Phillips', 'Wilson',
'Ali', 'Mason', 'Mitchell', 'Rose', 'Davis', 'Davies', 'Rodriguez', 'Cox', 'Alexander'
]
for i in range(len(first_names)):
user = User()
user.name = first_names[i] + " " + last_names[i]
user.email = first_names[i].lower() + "@example.com"
db.session.add(user)
db.session.commit()
return
if __name__ == '__main__':
# Build a sample db on the fly, if one does not exist yet.
app_dir = op.realpath(os.path.dirname(__file__))
database_path = op.join(app_dir, app.config['DATABASE_FILE'])
if not os.path.exists(database_path):
build_sample_db()
# Start app
app.run(debug=True, host='0.0.0.0')
I've found an ideal solution to my problem, overriding the index_view()
template-rendering view method in the flask_admin.contrib.sqla.ModelView
class.
class UserAdmin(ModelView):
column_searchable_list = ('name', 'email')
column_editable_list = ('name', 'email')
column_display_pk = True
can_set_page_size = True
# the number of entries to display in the list view
page_size = 5
# Now to override the index_view method
@expose('/')
def index_view(self):
"""List view overridden to DEFAULT to the last page,
if no other request args have been submitted"""
if self.can_delete:
delete_form = self.delete_form()
else:
delete_form = None
# Grab parameters from URL
view_args = self._get_list_extra_args()
# Map column index to column name
sort_column = self._get_column_by_idx(view_args.sort)
if sort_column is not None:
sort_column = sort_column[0]
# Get page size
page_size = view_args.page_size or self.page_size
#####################################################################
# Custom functionality to start on the last page instead of the first
if len(request.args) == 0:
# Standard request for the first page (no additional args)
count_query = self.get_count_query() if not self.simple_list_pager else None
# Calculate number of rows if necessary
count = count_query.scalar() if count_query else None
# Calculate number of pages
if count is not None and page_size:
num_pages = int(ceil(count / float(page_size)))
# Change the page to the last page (minus 1 for zero-based counting)
setattr(view_args, 'page', num_pages - 1)
# End of custom code. The rest below is from the Flask-Admin package
############################################################################
# Get database data for the view_args.page requested
count, data = self.get_list(view_args.page, sort_column, view_args.sort_desc,
view_args.search, view_args.filters, page_size=page_size)
list_forms = {}
if self.column_editable_list:
for row in data:
list_forms[self.get_pk_value(row)] = self.list_form(obj=row)
# Calculate number of pages
if count is not None and page_size:
num_pages = int(ceil(count / float(page_size)))
elif not page_size:
num_pages = 0 # hide pager for unlimited page_size
else:
num_pages = None # use simple pager
# Various URL generation helpers
def pager_url(p):
# Do not add page number if it is first page
if p == 0:
p = None
return self._get_list_url(view_args.clone(page=p))
def sort_url(column, invert=False, desc=None):
if not desc and invert and not view_args.sort_desc:
desc = 1
return self._get_list_url(view_args.clone(sort=column, sort_desc=desc))
def page_size_url(s):
if not s:
s = self.page_size
return self._get_list_url(view_args.clone(page_size=s))
# Actions
actions, actions_confirmation = self.get_actions_list()
if actions:
action_form = self.action_form()
else:
action_form = None
clear_search_url = self._get_list_url(view_args.clone(page=0,
sort=view_args.sort,
sort_desc=view_args.sort_desc,
search=None,
filters=None))
return self.render(
self.list_template,
data=data,
list_forms=list_forms,
delete_form=delete_form,
action_form=action_form,
# List
list_columns=self._list_columns,
sortable_columns=self._sortable_columns,
editable_columns=self.column_editable_list,
list_row_actions=self.get_list_row_actions(),
# Pagination
count=count,
pager_url=pager_url,
num_pages=num_pages,
can_set_page_size=self.can_set_page_size,
page_size_url=page_size_url,
page=view_args.page,
page_size=page_size,
default_page_size=self.page_size,
# Sorting
sort_column=view_args.sort,
sort_desc=view_args.sort_desc,
sort_url=sort_url,
# Search
search_supported=self._search_supported,
clear_search_url=clear_search_url,
search=view_args.search,
search_placeholder=self.search_placeholder(),
# Filters
filters=self._filters,
filter_groups=self._get_filter_groups(),
active_filters=view_args.filters,
filter_args=self._get_filters(view_args.filters),
# Actions
actions=actions,
actions_confirmation=actions_confirmation,
# Misc
enumerate=enumerate,
get_pk_value=self.get_pk_value,
get_value=self.get_list_value,
return_url=self._get_list_url(view_args),
# Extras
extra_args=view_args.extra_args,
)