如何转换 django 模板以使用 bootstrap-table 服务器端分页

How to convert a django template to use bootstrap-table server-side pagination

提问人:hepcat72 提问时间:2/5/2022 更新时间:3/24/2022 访问量:1056

问:

我有许多由 django 模板渲染的页面,我应用了 bootstrap-table 来实现列切换、客户端分页和多列排序。这是在创建了一个功能齐全的 django 模板之后。

我的表非常大,每列都有多个操作,例如:

  • 链接到网站上的其他页面
  • 数字格式
  • 水平对齐(例如右对齐数字)
  • 连接相关表中的值,由各种字符串分隔(例如逗号分隔)
  • 工具提示
  • 用“None”填充空值
  • 将 TimeDelta 转换为天或周 ...

许多操作都使用用 python 编写的simple_tags和过滤器。甚至还有一个模板使用 javascript 使用引导表事件(例如 )。$("#advsrchres").bootstrapTable({onAll: ...

我看到的每个示例都使用 bootstrap-table 的服务器端分页,没有模板,所有数据都是使用返回 JSON 的 “” 获取的。data-url

我希望我错了,但我的评估是,我必须用 javascript 或其他东西重写模板中的所有这些单元格装饰。我还没有开始研究如何做到这一点,所以在进行了许多徒劳无功的谷歌搜索之后,我在这里看看是否有人知道一种方法,不必完全重写那些巨大的 django 模板来实现服务器端分页?有没有办法告诉 bootstrap-table 将 JSON 中的数据插入到 django 模板中?

下面是其中一个模板的示例...

    <table class="table table-hover table-striped table-bordered"
        id="advsrchres"
        data-toggle="table"
        data-buttons-toolbar=".buttons-toolbar"
        data-buttons-class="primary"
        data-buttons-align="right"
        data-filter-control="false"
        data-search="false"
        data-show-search-clear-button="false"
        data-show-multi-sort="true"
        data-show-columns="true"
        data-show-columns-toggle-all="true"
        data-show-fullscreen="false"
        data-show-export="false"
        data-pagination="true">

        <colgroup span="8" class="identdata"></colgroup>
        <colgroup span="4" class="datadata"></colgroup>
        <colgroup span="12" class="metadata"></colgroup>

        <thead>
            <tr>
                <th data-valign="top" data-sortable="true" data-visible="false" data-sorter="alphanum" data-field="Animal" class="idgrp">Animal</th>
                <th data-valign="top" data-sortable="true" data-visible="true" data-sorter="alphanum" data-field="Sample" class="idgrp" data-switchable="false">Sample</th>
                <th data-valign="top" data-sortable="true" data-visible="true" data-sorter="alphanum" data-field="Tissue" class="idgrp">Tissue</th>
                <th data-valign="top" data-sortable="true" data-visible="false" data-sorter="alphanum" data-field="Peak_Group" class="idgrp">Peak Group</th>
                <th data-valign="top" data-sortable="true" data-visible="true" data-sorter="alphanum" data-field="Compound_Name" class="idgrp">Measured<br>Compound</th>
                <th data-valign="top" data-sortable="true" data-visible="false" data-sorter="alphanum" data-field="Compound_Synonym" class="idgrp">Measured<br>Compound<br>Synonym(s)</th>
                <th data-valign="top" data-sortable="true" data-visible="true" data-sorter="alphanum" data-field="Labeled_Element" class="idgrp">Labeled<br>Element</th>
                <th data-valign="top" data-sortable="true" data-visible="false" data-sorter="alphanum" data-field="Peak_Group_Set_Filename" class="idgrp">Peak Group Set Filename</th>

                <th data-valign="top" data-sortable="true" data-visible="true" data-sorter="numericOnly" data-field="Total_Abundance" class="datagrp" data-switchable="false">Total<br>Abundance</th>
                <th data-valign="top" data-sortable="true" data-visible="true" data-sorter="numericOnly" data-field="Enrichment_Fraction" class="datagrp">Enrichment<br>Fraction</th>
                <th data-valign="top" data-sortable="true" data-visible="true" data-sorter="numericOnly" data-field="Enrichment_Abundance" class="datagrp">Enrichment<br>Abundance</th>
                <th data-valign="top" data-sortable="true" data-visible="true" data-sorter="numericOnly" data-field="Normalized_Labeling" class="datagrp">Normalized<br>Labeling</th>

                <th data-valign="top" data-sortable="true" data-visible="false" data-sorter="alphanum" data-field="Formula" class="metagrp">Formula</th>
                <th data-valign="top" data-sortable="true" data-visible="true" data-sorter="alphanum" data-field="Genotype" class="metagrp">Genotype</th>
                <th data-valign="top" data-sortable="true" data-visible="false" data-sorter="alphanum" data-field="Sex" class="metagrp">Sex</th>
                <th data-valign="top" data-sortable="true" data-visible="true" data-sorter="alphanum" data-field="Feeding_Status" class="metagrp">Feeding<br>Status</th>
                <th data-valign="top" data-sortable="true" data-visible="false" data-sorter="alphanum" data-field="Diet" class="metagrp">Diet</th>
                <th data-valign="top" data-sortable="true" data-visible="true" data-sorter="alphanum" data-field="Treatment" class="metagrp">Treatment</th>
                <th data-valign="top" data-sortable="true" data-visible="false" data-sorter="numericOnly" data-field="Body_Weight" class="metagrp">Body<br>Weight<br>(g)</th>
                <th data-valign="top" data-sortable="true" data-visible="false" data-sorter="alphanum" data-field="Age" class="metagrp">Age<br>(weeks)</th>
                <th data-valign="top" data-sortable="true" data-visible="true" data-sorter="alphanum" data-field="Tracer_Compound" class="metagrp" data-switchable="false">Tracer<br>Compound</th>
                <th data-valign="top" data-sortable="true" data-visible="false" data-sorter="numericOnly" data-field="Tracer_Infusion_Rate" class="metagrp">Tracer<br>Infusion<br>Rate<br>(ul/min/g)</th>
                <th data-valign="top" data-sortable="true" data-visible="false" data-sorter="numericOnly" data-field="Tracer_Infusion_Concentration" class="metagrp">Tracer<br>Infusion<br>Concentration<br>(mM)</th>
                <th data-valign="top" data-sortable="true" data-visible="true" data-sorter="alphanum" data-field="Study" class="metagrp">Studies</th>
            </tr>
        </thead>

        <tbody>

            {% for pg in res.all %}



... SNIP ... below shows a sample of 6 of the 24 columns in this particular template 



                    <!-- Body Weight (g) -->
                    <td class="text-end">
                        {{ pg.msrun.sample.animal.body_weight }}
                    </td>

                    <!-- Age (weeks) -->
                    <td class="text-end">
                        <p title="{{ pg.msrun.sample.animal.age }} (d-hh:mm:ss)">{{ pg.msrun.sample.animal.age|durationToWeeks|decimalPlaces:2 }}</p>
                    </td>

                    <!-- Tracer Compound -->
                    <td>
                        {% if pg.msrun.sample.animal.tracer_compound is None %}
                            <!-- Put displayed link text first for sorting -->
                            <div style="display:none;">None</div>
                            <p title="Animal has no tracer.">None</p>
                        {% else %}
                            <!-- Put displayed link text first for sorting -->
                            <div style="display:none;">{{ pg.msrun.sample.animal.tracer_compound.name }}</div>
                            <a href="{% url 'compound_detail' pg.msrun.sample.animal.tracer_compound.id %}">
                                {{ pg.msrun.sample.animal.tracer_compound.name }}
                            </a>
                        {% endif %}
                    </td>

                    <!-- Tracer Infusion Rate (ul/min/g) -->
                    <td class="text-end">
                        {{ pg.msrun.sample.animal.tracer_infusion_rate }}
                    </td>

                    <!-- Tracer Infusion Concentration (mM) -->
                    <td class="text-end">
                        {{ pg.msrun.sample.animal.tracer_infusion_concentration }}
                    </td>

                    <!-- Studies -->
                    <td>
                        <!-- Put displayed link text first for sorting -->
                        <div style="display:none;">
                            {% define True as first %}
                            {% for study in pg.msrun.sample.animal.studies.all %}{% if not first %},<br>{% endif%}{{ study.name }}{% define False as first %}{% endfor %}
                        </div>

                        {% define True as first %}
                        {% for study in pg.msrun.sample.animal.studies.all %}{% if not first %},<br>{% endif%}<a href="{% url 'study_detail' study.id %}">{{ study.name }}</a>{% define False as first %}{% endfor %}
                    </td>
            {% endfor %}
        </tbody>
Django 分页 服务器端 引导表

评论


答:

0赞 hepcat72 3/24/2022 #1

这里有几点需要注意。

  1. 我没有指出(因为我没有想到它会是相关的)我显示的数据是搜索的结果。Bootstrap-table 的服务器端分页仅支持静态数据,因此弄清楚如何使用模板是没有意义的。
  2. 虽然你不能使用任何 bootstrap-table 内置的服务器端分页功能,但它确实提供了分页控件的各种装饰,因此当你实现自己的服务器端分页时,它至少会利用 BST 的 CSS。
  3. Django 的内置分页器工具仅适用于单个模型,不能应用于结果不等于 1 条记录 = 1 行的搜索。

因此,我最终实现了自己的分页器类,并利用了 BST 的分页器控制装饰。我不会详细介绍实现的细节,因为有很多选项,但为了在你拥有以下条件时滚动你自己的分页器:

  1. 搜索结果的表
  2. 记录≠行(即它们不是 1:1)
  3. 联接记录

这些基本上是您需要的:

  • 对于搜索
    • 定义搜索参数的隐藏表单字段(我使用了 JSONField)
    • 可选控件的任何其他字段
  • 每页行数的表单字段
  • 要导航到的页码的表单字段
  • 并且(可选):
    • 一个字段,指示要按其排序的字段
    • 排序方向的字段

当你使用 Django ORM 进行搜索时,你只需要根据页码和每页的行数/记录来对结果进行切片。

在这方面,Django 有一些需要注意的怪癖:

虽然您可以使用 (with ) 来模拟真正的 SQL 左连接(您可以在其中获取重复的“根表”记录),但您必须处理一些限制:.distinct(fields).order_by(field)

  1. 如果要计算列中唯一值的计数以提供某些统计信息,则不能使用,因为最终会出现异常。.annotate(Count(...))NotImplemented
  2. 在视图代码中,您可以通过关系访问“真正的左连接”,但在模板中,您没有,并且您必须遍历每个重复根表记录的所有可能的相关记录(例如 )。但是,您可以在视图中使用以下方法解决此限制,例如: 。然后在模板中,我只是使用模板标签从属于该行的特定记录中检索特定记录。M:M{% for rootTable.MMrecs.all }.annotate().annotate("myMMtablerec"=F("MMrecs__pk"))rootTable.MMrecs.all