Files
allianceauth/docs/development/custom/framework/datatables.md
2026-01-20 08:22:25 +00:00

7.3 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 %}
{% load aa_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>
    </table>
{% endblock content %}

{% block extra_css %}
    {% include "bundles/datatables-2-css-bs5.html" %}

    {% comment %} If you don't use the ColumnControl Extension, remove the next line {% endcomment %}
    {% include "bundles/datatables-2-columncontrol-css-bs5.html" %}
{% endblock %}

{% block extra_javascript %}
    {% get_datatables_language_static LANGUAGE_CODE as DT_LANG_PATH %}

    {% include "bundles/datatables-2-js-bs5.html" %}

    {% comment %} If you don't use the ColumnControl Extension, remove the next line {% endcomment %}
    {% include "bundles/datatables-2-columncontrol-js-bs5.html" %}

    <script>
        $(document).ready(() => {
            // Assuming you have a table with the ID 'table'
            // A jQuery HTML Element `$('#table')` can also be passed instead of a selector string
            const dt = new DataTable('#table', {
                language: {
                    url: '{{ DT_LANG_PATH }}',
                    // Important: The value for `language.processing` must be passed
                    // as a JS template string, not as a normal string. This is to
                    // allow for Django template rendering inside the processing
                    // indicator.
                    processing: `{% include "framework/datatables/process-indicator.html" %}`
                },
                layout: { // See: https://datatables.net/reference/option/layout
                    topStart: 'pageLength',
                    topEnd: 'search',
                    bottomStart: 'info',
                    bottomEnd: 'paging'
                },
                ordering: { // See: https://datatables.net/reference/option/ordering
                    handler: true, // Enable ordering by clicking on column headers
                    indicators: false, // Disable ordering indicators on column headers (important when ColumnControl is used)
                },
                processing: true, // Show processing indicator when loading data
                serverSide: true, // Enable server-side processing
                ajax: '{% url "appname:data_table_view" %}',
                columnDefs: [
                    {
                        targets: [0],
                        columnControl: [],
                        sortable: false,
                        searchable: false
                    },
                    {
                        targets: [1,2,3],
                        columnControl: [
                            {
                                target: 0,
                                content: []
                            },
                            {
                                target: 1,
                                content: ['search']
                            }
                        ],
                    }
                ],
                order: [
                    [1, "asc"]
                ],
                pageLength: 10, // Override default page length if desired
                responsive : true
            });
        });
    </script>
{% endblock extra_javascript %}

Add our Views

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

Columns definition

The columns must be defined as a 2 part tuple

  • Part 1 is the database field that will be used for filtering and ordering. If this is a foreign key you need to point to a field that is compatible with __icontains like charField or textField. It can be None/False/"" if no ordering for filtering is required for this row.
    • Examples for the EveCharacter Model:
      • character_name
      • character_ownership__user__username
      • character_ownership__user__profile__main_character__character_name
  • Part 2 is a string that is used to the render the column for each row. This can be a html stub or a string containing django style template language.
    • Examples for the EveCharacter Model
      • {{ row.character_name }}
      • {{ row.character_ownership.user.username }}
      • {{ row.character_ownership.user.profile.main_character.character_name }}
      • appname/stubs/character_img.html

appname/views.py

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='data_table_view')
]

and you are done.