# 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

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

### template/appname/stubs/name.html

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

### template/appname/stubs/corp.html

```django
{{ row.corporation_name }}
```

### template/appname/list.html

```django
{% 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>
    </table>
{% endblock content %}
{% block extra_css %}
    <link rel="stylesheet" href="https://cdn.datatables.net/2.3.6/css/dataTables.bootstrap5.css" />
    <link href="https://cdn.datatables.net/columncontrol/1.2.0/css/columnControl.bootstrap5.min.css" rel="stylesheet">
{% endblock %}
{% block extra_javascript %}
    <script src="https://cdn.datatables.net/2.3.6/js/dataTables.js"></script>
    <script src="https://cdn.datatables.net/2.3.6/js/dataTables.bootstrap5.js"></script>
    <script src="https://cdn.datatables.net/columncontrol/1.2.0/js/dataTables.columnControl.min.js"></script>
    <script>
        $(document).ready(function() {
            $('#table').DataTable({
                serverSide: true,
                ajax: '{% url "appname:table" %}',
                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,
                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

```python
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

```python
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.
