{% extends "list_view.html" %} {% load i18n %} {% comment %} Overrides the outer `content` block, not page.header/page.content-wrapper. Those two sit inside page_view.html's , a plain block div between and that breaks the flex chain the height depends on — from there the filled layout cannot be built at all (specs/027-table-layout-and-column-styling/research.md R5). This view writes its own markup instead: the container and the two toolbars are bypassed, not reused. All six block names page_view.html declares are re-declared below, in positions that make sense for this layout, so a project overriding any of them on a table view keeps working. page.actions does not use {{ block.super }}, because its default (list_view.html's search/sort/filter/create) includes sort, which a table view's own action set must not (FR-007) — see MVPTableViewMixin.actions. The breadcrumbs live inside the title bar rather than on a row of their own. On a full-screen table the vertical budget is the whole point, and the trail was spending a row to end in the same word as the heading below it. So the trail carries the page's identity and its last crumb *is* the heading: the

moves into the final
  • , which keeps one heading per page rather than trading accessibility for a row. page.header is the block that renders it. Written out here rather than as a component. It is markup for one view, which is the case Article XI exempts, and one caller does not justify the abstraction Article III warns about. If a second view ever wants this bar, it becomes a component then. The bars are plain flex rows rather than , for two reasons. A toolbar sizes each slot to its content, but these bars have to span the table's full width to put the actions and the pagination at the trailing edge. And a toolbar renders {{ actions }} in its trailing slot, where a Cotton slot falls through to the context variable of the same name when the caller fills no slot — so any page whose context carries an `actions` key prints its repr there. The context key is `table_actions` now, but a bare row keeps the trap shut whatever a project puts in its own context. Padding sits on the bars, never on the page. The table is meant to reach the edges of the space the shell gives it: its scrollbar hugs the shell edge and its rows use the full width. Padding on would inset the scroll region along with everything else. runs at gap="0" for the same reason — a gap is space the bars do not control, and the table should meet them rather than float between them. The bars own their own spacing. app.footer is emptied. The shell's footer belongs to a page that ends; this one does not — the viewport is fully spent on the table, and a footer under it either steals rows or never comes into view. It is blanked here rather than turned off in config, so it stays a per-view decision. {% endcomment %} {% block app.footer %}{% endblock app.footer %} {% block content %} {% block page.content-wrapper %}
    {% block page.title %}
    {% block page.header %} {% for crumb in page.breadcrumbs|slice:":-1" %} {% endfor %}

    {{ page.title }}

    {% endblock page.header %} {% if page.subtitle %}

    {{ page.subtitle }}

    {% endif %}
    {% block page.actions %} {% endblock page.actions %}
    {% endblock page.title %}
    {% block page.content %} {% endblock page.content %}
    {% block page.footer %} {% if page_obj %}
    {% with start=page_obj.start_index end=page_obj.end_index total=page_obj.paginator.count %} {% blocktrans with name_plural=model_info.verbose_name_plural %} Showing {{ start }}-{{ end }} of {{ total }} {{ name_plural }} {% endblocktrans %} {% endwith %}
    {% endif %} {% endblock page.footer %}
    {% endblock page.content-wrapper %}
    {% endblock content %}