Files
allianceauth/docs/development/custom/framework/datatables.md
Aaron Kable 1c7c775029 refactor
2026-01-08 08:50:54 +08:00

5.6 KiB

DataTables Server Side Rendering

The allianceauth.framework.datatables.DataTablesView module provides a simple class based view to implement simple server side filtering ordering and searching of DataTables.

This is intended to make the life of our community apps developer a little easier, so they don't have to reinvent the wheel.

Usage

To use this view is as easy as defining your stub templates, and fields and adding the view to the urls.py

Given the EveCharacter Model as our model of choice we would define our stubs like so

Add our Templates

template/appname/stubs/icon.html

{% load evelinks %}
{% character_portrait_url row 32 %}

template/appname/stubs/name.html

{{ row.character_name }} <span class="text-small">({{ row.character_ownership.user.username }})</span>

template/appname/stubs/corp.html

{{ row.corporation_name }}

template/appname/list.html

{% extends "allianceauth/base-bs5.html" %}
{% load i18n %}
{% block page_title %}
    {% translate "App Name" %}
{% endblock page_title %}
{% block content %}
    <table class="table table-striped w-100" id="table">
        <!-- Normal Header Rows -->
        <thead>
            <tr>
                <th></th>
                <th>{% translate "Name" %}</th>
                <th>{% translate "Corporation" %}</th>
                <th>{% translate "Alliance" %}</th>
            </tr>
        </thead>
        <!-- Put the footer in the header so it doesn't trigger the sorting on searching -->
        <!-- only footers with text will get given an input -->
        <!-- This is just an option, you do it however you like -->
        <tfoot style="display: table-header-group;">
            <tr>
                <th></th>
                <th>{% translate "Name" %}</th>
                <th>{% translate "Corporation" %}</th>
                <th>{% translate "Alliance" %}</th>
            </tr>
        </tfoot>
        <!-- Empty tbody -->
        <tbody>
        </tbody>
    </table>
{% endblock content %}
{% block extra_css %}
    {% include 'bundles/datatables-css-bs5.html' %}
{% endblock %}
{% block extra_javascript %}
    {% include 'bundles/datatables-js-bs5.html' %}
    <script>
        $(document).ready(function() {
            $('#table').DataTable({
                serverSide: true,
                ajax: '{% url "appname:table" %}',
                // This is for the column searching
                initComplete: function () {
                    this.api()
                    .columns()
                    .every(function () {
                        let column = this;
                        let title = column.footer().textContent;
                        if (title != ""){
                            // Create input element
                            let input = document.createElement('input');
                            input.classList.add("w-100")
                            input.placeholder = title;
                            column.footer().replaceChildren(input);

                            // Event listener for user input
                            input.addEventListener('keyup', () => {
                                if (column.search() !== this.value) {
                                    column.search(input.value).draw();
                                }
                            });
                        }
                    });
                },
                columnDefs: [
                    { "searchable": false, "targets": [0] },
                    { "sortable": false, "targets": [0] },
                ],
                order: [
                    [1, "asc"]
                ],
                pageLength: 10,
                responsive : true
            });
        });
    </script>
{% endblock extra_javascript %}

Add our Views

Then we can setup out view in our appname/views.py file.

from django.shortcuts import render
# Alliance Auth
from allianceauth.framework.datatables import DataTablesView
from allianceauth.eveonline.models import EveCharacter

## Datatables server side view
class EveCharacterTable(DataTablesView):
    model = EveCharacter

    # Define the columns as a tuple.
    # String for field name for filtering and ordering
    # String for the render template
    # Templates can be a html file or template language directly in the list below
    columns = [
        # ("field_for_queries_or_sort", template: str)
        ("", "appname/stubs/icon.html"),
        ("character_name", "appname/stubs/name.html"),
        ("corporation_name", "appname/stubs/corp.html"),
        ("alliance_name", "{{ row.alliance_name }} {{ row.alliance_id }}"),
    ]

    # if you need to do some prefetch or pre-filtering you can overide this function
    def get_model_qs(self, request: HttpRequest):
        qs = self.model.objects
        if not request.user.is_superuser:
            # eg only show unlinked characters to non-superusers
            # just an example
            # filtering here will prevent people searching things that may not be visible to them
            qs = qs.filter(character_ownership__isnull=True)
        # maybe some character ownership select related for performance?
        return qs.select_related("character_ownership", "character_ownership__user")

## Main Page View
def show_table(request):
    return render("appname/list.html")

Add our Urls

appname/urls.py

from django.urls import path

from . import views

app_name = 'appname'

urlpatterns = [
    path("list/", views.EveCharacterTable.as_view(), name='eve_character_table'),
    path("tables/data_table", views.show_table, name='table')
]

and you are done.