How do you add a summary row for Flask-Admin?

倖福魔咒の 提交于 2019-12-20 03:24:06

问题


In my flask-admin index_view, I am displaying financial information for my rows.

I would like to add an extra row, "summary row", at the bottom of my index_view table which sums up all the columns.

How can I accomplish this?


回答1:


There's a couple of things you need to do. Provide a custom list.html template and override the render() method for the view. In the render method inject your summary data into the kwargs and in the custom template use the summary data to output appropriate html. You could add the summary data to the end of the existing data table or add it to a separate table, as seen in the example below.

Here's a self-contained example (two files) using Flask-Admin 1.5, SQLAlchemy and SQLite. custom_list.html is taken directly from flask-admin list.html and we are manipulating the block beginning at line 68:

{% block model_list_table %}
    ...
{% endblock %}

Note that in the render() method the summary data is an array of dicts. Each dict has a 'title' attribute (e.g 'title: 'Page Total' plus an attribute for each of the columns summary data is required.

app.py

import random
import string

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_admin.contrib.sqla import ModelView
from flask_admin import Admin, expose

# Create application
from sqlalchemy import func

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_db.sqlite'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + app.config['DATABASE_FILE']
db = SQLAlchemy(app)


# Flask views
@app.route('/')
def index():
    return '<a href="/admin/">Click me to get to Admin!</a>'


class Project(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(255), nullable=False, unique=True)
    cost = db.Column(db.Integer(), nullable=False)

    def __str__(self):
        return unicode(self).encode('utf-8')

    def __unicode__(self):
        return "Name: {name}; Cost : {cost}".format(name=self.name, cost=self.cost)


class ProjectView(ModelView):
    # don't call the custom page list.html as you'll get a recursive call
    list_template = 'admin/model/custom_list.html'
    form_columns = ('name', 'cost')
    page_size = 5

    def page_cost(self, current_page):
        # this should take into account any filters/search inplace
        _query = self.session.query(Project).limit(self.page_size).offset(current_page * self.page_size)
        return sum([p.cost for p in _query])

    def total_cost(self):
        # this should take into account any filters/search inplace
        return self.session.query(func.sum(Project.cost)).scalar()

    def render(self, template, **kwargs):
        # we are only interested in the list page
        if template == 'admin/model/custom_list.html':
            # append a summary_data dictionary into kwargs
            _current_page = kwargs['page']
            kwargs['summary_data'] = [
                {'title': 'Page Total', 'name': None, 'cost': self.page_cost(_current_page)},
                {'title': 'Grand Total', 'name': None, 'cost': self.total_cost()},
            ]
        return super(ProjectView, self).render(template, **kwargs)


admin = Admin(app, template_mode="bootstrap3")
admin.add_view(ProjectView(Project, db.session))


def build_sample_db():
    db.drop_all()
    db.create_all()

    for _ in range(0, 100):
        _cost = random.randrange(1, 1000)
        _project = Project(
            name=''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(10)),
            cost=_cost
        )
        db.session.add(_project)

    db.session.commit()


if __name__ == '__main__':
    build_sample_db()
    app.run(port=5000, debug=True)

templates/admin/model/custom_list.html

{% extends 'admin/model/list.html' %}

{% block model_list_table %}
<div class="table-responsive">
<table class="table table-striped table-bordered table-hover model-list">
    <thead>
        <tr>
            {% block list_header scoped %}
                {% if actions %}
                <th class="list-checkbox-column">
                    <input type="checkbox" name="rowtoggle" class="action-rowtoggle" title="{{ _gettext('Select all records') }}" />
                </th>
                {% endif %}
                {% block list_row_actions_header %}
                    {% if admin_view.column_display_actions %}
                    <th class="col-md-1">&nbsp;</th>
                    {% endif %}
                {% endblock %}
                {% for c, name in list_columns %}
                {% set column = loop.index0 %}
                <th class="column-header col-{{c}}">
                    {% if admin_view.is_sortable(c) %}
                        {% if sort_column == column %}
                            <a href="{{ sort_url(column, True) }}" title="{{ _gettext('Sort by %(name)s', name=name) }}">
                                {{ name }}
                                {% if sort_desc %}
                                    <span class="fa fa-chevron-up glyphicon glyphicon-chevron-up"></span>
                                {% else %}
                                    <span class="fa fa-chevron-down glyphicon glyphicon-chevron-down"></span>
                                {% endif %}
                            </a>
                        {% else %}
                            <a href="{{ sort_url(column) }}" title="{{ _gettext('Sort by %(name)s', name=name) }}">{{ name }}</a>
                        {% endif %}
                    {% else %}
                        {{ name }}
                    {% endif %}
                    {% if admin_view.column_descriptions.get(c) %}
                        <a class="fa fa-question-circle glyphicon glyphicon-question-sign"
                           title="{{ admin_view.column_descriptions[c] }}"
                           href="javascript:void(0)" data-role="tooltip"
                        ></a>
                    {% endif %}
                </th>
                {% endfor %}
            {% endblock %}
        </tr>
    </thead>
    {% for row in data %}
    <tr>
        {% block list_row scoped %}
            {% if actions %}
            <td>
                <input type="checkbox" name="rowid" class="action-checkbox" value="{{ get_pk_value(row) }}" title="{{ _gettext('Select record') }}" />
            </td>
            {% endif %}
            {% block list_row_actions_column scoped %}
                {% if admin_view.column_display_actions %}
                <td class="list-buttons-column">
                    {% block list_row_actions scoped %}
                      {% for action in list_row_actions %}
                      {{ action.render_ctx(get_pk_value(row), row) }}
                      {% endfor %}
                    {% endblock %}
                </td>
                {%- endif -%}
            {% endblock %}

            {% for c, name in list_columns %}
                <td class="col-{{c}}">
                {% if admin_view.is_editable(c) %}
                    {% set form = list_forms[get_pk_value(row)] %}
                    {% if form.csrf_token %}
                    {{ form[c](pk=get_pk_value(row), display_value=get_value(row, c), csrf=form.csrf_token._value()) }}
                    {% else %}
                    {{ form[c](pk=get_pk_value(row), display_value=get_value(row, c)) }}
                    {% endif %}
                {% else %}
                {{ get_value(row, c) }}
                {% endif %}
                </td>
            {% endfor %}
        {% endblock %}
    </tr>
    {% else %}
    <tr>
        <td colspan="999">
            {% block empty_list_message %}
            <div class="text-center">
                {{ admin_view.get_empty_list_message() }}
            </div>
            {% endblock %}
        </td>
    </tr>
    {% endfor %}
</table>
</div>

<h3>Summaries</h3>

<div class="table-responsive">
    <table class="table table-striped table-bordered table-hover model-list">
        <thead>
            <tr>
                {% if actions %}
                <th class="list-checkbox-column">
                </th>
                {% endif %}

                <th class="col-md-1"></th>
                {% for c, name in list_columns %}
                    {% set column = loop.index0 %}
                    <th class="column-header col-{{c}}">
                        {{ name }}
                    </th>
                {% endfor %}
            </tr>
        </thead>
        {% for row in summary_data %}
            <tr>
                <td colspan="2"><strong>{{ row['title'] or ''}}</strong></td>
                {% for c, name in list_columns %}
                    <td class="col-{{c}}">
                        {{ row[c] or ''}}
                    </td>
                {% endfor %}
            </tr>
        {% endfor %}
    </table>
</div>


{% 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 %}


来源:https://stackoverflow.com/questions/47124728/how-do-you-add-a-summary-row-for-flask-admin

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!