mirror of
https://gitlab.com/allianceauth/allianceauth.git
synced 2026-02-14 11:06:23 +01:00
Compare commits
5 Commits
6c2cbb069f
...
4799e0beb1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4799e0beb1 | ||
|
|
a4cce84f67 | ||
|
|
edf5b7bb8c | ||
|
|
7e338d09a2 | ||
|
|
775db62c7a |
@@ -43,7 +43,7 @@ def defaultdict_to_dict(d):
|
|||||||
class DataTablesView(View):
|
class DataTablesView(View):
|
||||||
|
|
||||||
model: Model = None
|
model: Model = None
|
||||||
columns: list[tuple] = []
|
columns: List[tuple] = []
|
||||||
|
|
||||||
def get_model_qs(self, request: HttpRequest, *args, **kwargs):
|
def get_model_qs(self, request: HttpRequest, *args, **kwargs):
|
||||||
return self.model.objects
|
return self.model.objects
|
||||||
@@ -53,22 +53,25 @@ class DataTablesView(View):
|
|||||||
filter_qs = Q()
|
filter_qs = Q()
|
||||||
for id, c in table_conf["columns"].items():
|
for id, c in table_conf["columns"].items():
|
||||||
_c = self.columns[int(id)][0]
|
_c = self.columns[int(id)][0]
|
||||||
if c["searchable"] and len(_c) > 0:
|
if c.get("searchable", False) and len(_c) > 0:
|
||||||
if c["columnControl"]:
|
if c.get("columnControl", False):
|
||||||
_sv = str(c["columnControl"]["search"]["value"])
|
_sv = str(c["columnControl"]["search"]["value"])
|
||||||
|
"""contains, equal, ends, starts, empty"""
|
||||||
_logic = str(c["columnControl"]["search"]["logic"])
|
_logic = str(c["columnControl"]["search"]["logic"])
|
||||||
|
"""text, date, num"""
|
||||||
_type = str(c["columnControl"]["search"]["type"])
|
_type = str(c["columnControl"]["search"]["type"])
|
||||||
if len(_sv) > 0:
|
if _logic == "empty":
|
||||||
|
filter_qs &= Q(**{f'{_c}': ""})
|
||||||
|
elif len(_sv) > 0:
|
||||||
if _logic == "contains":
|
if _logic == "contains":
|
||||||
filter_qs |= Q(**{f'{_c}__icontains': _sv})
|
filter_qs &= Q(**{f'{_c}__icontains': _sv})
|
||||||
elif _logic == "starts":
|
elif _logic == "starts":
|
||||||
filter_qs |= Q(**{f'{_c}__istartswith': _sv})
|
filter_qs &= Q(**{f'{_c}__istartswith': _sv})
|
||||||
elif _logic == "ends":
|
elif _logic == "ends":
|
||||||
filter_qs |= Q(**{f'{_c}__iendswith': _sv})
|
filter_qs &= Q(**{f'{_c}__iendswith': _sv})
|
||||||
elif _logic == "equal":
|
elif _logic == "equal":
|
||||||
filter_qs |= Q(**{f'{_c}': _sv})
|
filter_qs &= Q(**{f'{_c}': _sv})
|
||||||
elif _logic == "empty":
|
|
||||||
filter_qs |= Q(**{f'{_c}': ""})
|
|
||||||
else:
|
else:
|
||||||
_sv = str(c["search"]["value"])
|
_sv = str(c["search"]["value"])
|
||||||
if len(_sv) > 0:
|
if len(_sv) > 0:
|
||||||
@@ -81,6 +84,28 @@ class DataTablesView(View):
|
|||||||
filter_qs |= Q(**{f'{_c}__icontains': _gsv})
|
filter_qs |= Q(**{f'{_c}__icontains': _gsv})
|
||||||
return filter_qs
|
return filter_qs
|
||||||
|
|
||||||
|
def except_qs(self, table_conf: dict):
|
||||||
|
# Search
|
||||||
|
except_qs = Q()
|
||||||
|
for id, c in table_conf["columns"].items():
|
||||||
|
_c = self.columns[int(id)][0]
|
||||||
|
if c.get("searchable", False) and len(_c) > 0:
|
||||||
|
if c.get("columnControl", False):
|
||||||
|
_sv = str(c["columnControl"]["search"]["value"])
|
||||||
|
"""notContains, notEqual, notEmpty"""
|
||||||
|
_logic = str(c["columnControl"]["search"]["logic"])
|
||||||
|
"""text, date, num"""
|
||||||
|
_type = str(c["columnControl"]["search"]["type"])
|
||||||
|
if _logic == "notEmpty":
|
||||||
|
except_qs |= Q(**{f'{_c}': ""})
|
||||||
|
elif len(_sv) > 0:
|
||||||
|
if _logic == "notContains":
|
||||||
|
except_qs |= Q(**{f'{_c}__icontains': _sv})
|
||||||
|
elif _logic == "notEqual":
|
||||||
|
except_qs |= Q(**{f'{_c}': _sv})
|
||||||
|
|
||||||
|
return except_qs
|
||||||
|
|
||||||
def get_table_config(self, get: dict):
|
def get_table_config(self, get: dict):
|
||||||
_cols = nested_param_dict()
|
_cols = nested_param_dict()
|
||||||
for c, v in get.items():
|
for c, v in get.items():
|
||||||
@@ -134,6 +159,8 @@ class DataTablesView(View):
|
|||||||
**kwargs
|
**kwargs
|
||||||
).filter(
|
).filter(
|
||||||
self.filter_qs(table_conf)
|
self.filter_qs(table_conf)
|
||||||
|
).exclude(
|
||||||
|
self.except_qs(table_conf)
|
||||||
).order_by(
|
).order_by(
|
||||||
*self.get_order(table_conf)
|
*self.get_order(table_conf)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -74,10 +74,18 @@ class TestDataTables(TestCase):
|
|||||||
cls.user.save()
|
cls.user.save()
|
||||||
|
|
||||||
EveCharacter.objects.all().delete()
|
EveCharacter.objects.all().delete()
|
||||||
for i in range(1,21):
|
for i in range(1,16):
|
||||||
EveCharacter.objects.create(
|
EveCharacter.objects.create(
|
||||||
character_id=1000+i,
|
character_id=1000+i,
|
||||||
character_name=f"{1000+i} - Test Character",
|
character_name=f"{1000+i} - Test Character - {1000+i}",
|
||||||
|
corporation_id=2000+i,
|
||||||
|
corporation_name=f"{2000+i} - Test Corporation",
|
||||||
|
)
|
||||||
|
|
||||||
|
for i in range(16,21):
|
||||||
|
EveCharacter.objects.create(
|
||||||
|
character_id=1000+i,
|
||||||
|
character_name=f"{1000+i} - Test Character - {1000+i}",
|
||||||
corporation_id=2000+i,
|
corporation_id=2000+i,
|
||||||
corporation_name=f"{2000+i} - Test Corporation",
|
corporation_name=f"{2000+i} - Test Corporation",
|
||||||
alliance_id=3000+i,
|
alliance_id=3000+i,
|
||||||
@@ -104,6 +112,17 @@ class TestDataTables(TestCase):
|
|||||||
self.assertEqual(data[0][0], "1020")
|
self.assertEqual(data[0][0], "1020")
|
||||||
self.assertEqual(data[9][0], "1011")
|
self.assertEqual(data[9][0], "1011")
|
||||||
|
|
||||||
|
def test_view_no_sort(self):
|
||||||
|
self.get_params.pop("order[0][column]")
|
||||||
|
self.get_params.pop("order[0][dir]")
|
||||||
|
self.client.force_login(self.user)
|
||||||
|
request = self.factory.get('/fake-url/', data=self.get_params)
|
||||||
|
response = TestView()
|
||||||
|
response.setup(request)
|
||||||
|
data = json.loads(response.get(request).content)["data"]
|
||||||
|
self.assertEqual(data[0][0], "1001")
|
||||||
|
self.assertEqual(data[9][0], "1010")
|
||||||
|
|
||||||
def test_view_non_sortable_sort(self):
|
def test_view_non_sortable_sort(self):
|
||||||
self.get_params["order[0][dir]"] = "desc"
|
self.get_params["order[0][dir]"] = "desc"
|
||||||
self.get_params["order[0][column]"] = "0"
|
self.get_params["order[0][column]"] = "0"
|
||||||
@@ -147,9 +166,104 @@ class TestDataTables(TestCase):
|
|||||||
|
|
||||||
def test_view_col_1_search_empty(self):
|
def test_view_col_1_search_empty(self):
|
||||||
self.get_params["columns[1][search][value]"] = "zzz"
|
self.get_params["columns[1][search][value]"] = "zzz"
|
||||||
|
|
||||||
self.client.force_login(self.user)
|
self.client.force_login(self.user)
|
||||||
request = self.factory.get('/fake-url/', data=self.get_params)
|
request = self.factory.get('/fake-url/', data=self.get_params)
|
||||||
response = TestView()
|
response = TestView()
|
||||||
response.setup(request)
|
response.setup(request)
|
||||||
data = json.loads(response.get(request).content)["data"]
|
data = json.loads(response.get(request).content)["data"]
|
||||||
self.assertEqual(len(data), 0)
|
self.assertEqual(len(data), 0)
|
||||||
|
|
||||||
|
def test_view_cc_3_search_empty(self):
|
||||||
|
self.get_params["columns[3][columnControl][search][value]"] = ""
|
||||||
|
self.get_params["columns[3][columnControl][search][logic]"] = "empty"
|
||||||
|
self.get_params["columns[3][columnControl][search][type]"] = "text"
|
||||||
|
self.get_params["length"] = "20"
|
||||||
|
|
||||||
|
self.client.force_login(self.user)
|
||||||
|
request = self.factory.get('/fake-url/', data=self.get_params)
|
||||||
|
response = TestView()
|
||||||
|
response.setup(request)
|
||||||
|
data = json.loads(response.get(request).content)["data"]
|
||||||
|
self.assertEqual(len(data), 15)
|
||||||
|
|
||||||
|
def test_view_cc_3_search_not_empty(self):
|
||||||
|
self.get_params["columns[3][columnControl][search][value]"] = ""
|
||||||
|
self.get_params["columns[3][columnControl][search][logic]"] = "notEmpty"
|
||||||
|
self.get_params["columns[3][columnControl][search][type]"] = "text"
|
||||||
|
self.client.force_login(self.user)
|
||||||
|
request = self.factory.get('/fake-url/', data=self.get_params)
|
||||||
|
response = TestView()
|
||||||
|
response.setup(request)
|
||||||
|
data = json.loads(response.get(request).content)["data"]
|
||||||
|
self.assertEqual(len(data), 5)
|
||||||
|
|
||||||
|
def test_view_cc_1_search_ends_with(self):
|
||||||
|
self.get_params["columns[1][columnControl][search][value]"] = "9"
|
||||||
|
self.get_params["columns[1][columnControl][search][logic]"] = "ends"
|
||||||
|
self.get_params["columns[1][columnControl][search][type]"] = "text"
|
||||||
|
self.client.force_login(self.user)
|
||||||
|
request = self.factory.get('/fake-url/', data=self.get_params)
|
||||||
|
response = TestView()
|
||||||
|
response.setup(request)
|
||||||
|
data = json.loads(response.get(request).content)["data"]
|
||||||
|
self.assertEqual(len(data), 2)
|
||||||
|
|
||||||
|
def test_view_cc_1_search_starts_with(self):
|
||||||
|
self.get_params["columns[1][columnControl][search][value]"] = "1009"
|
||||||
|
self.get_params["columns[1][columnControl][search][logic]"] = "starts"
|
||||||
|
self.get_params["columns[1][columnControl][search][type]"] = "text"
|
||||||
|
self.client.force_login(self.user)
|
||||||
|
request = self.factory.get('/fake-url/', data=self.get_params)
|
||||||
|
response = TestView()
|
||||||
|
response.setup(request)
|
||||||
|
data = json.loads(response.get(request).content)["data"]
|
||||||
|
self.assertEqual(len(data), 1)
|
||||||
|
|
||||||
|
def test_view_cc_1_search_not_contains(self):
|
||||||
|
self.get_params["columns[1][columnControl][search][value]"] = "100"
|
||||||
|
self.get_params["columns[1][columnControl][search][logic]"] = "notContains"
|
||||||
|
self.get_params["columns[1][columnControl][search][type]"] = "text"
|
||||||
|
self.get_params["length"] = "20"
|
||||||
|
self.client.force_login(self.user)
|
||||||
|
request = self.factory.get('/fake-url/', data=self.get_params)
|
||||||
|
response = TestView()
|
||||||
|
response.setup(request)
|
||||||
|
data = json.loads(response.get(request).content)["data"]
|
||||||
|
self.assertEqual(len(data), 11)
|
||||||
|
|
||||||
|
def test_view_cc_1_search_contains(self):
|
||||||
|
self.get_params["columns[1][columnControl][search][value]"] = "100"
|
||||||
|
self.get_params["columns[1][columnControl][search][logic]"] = "contains"
|
||||||
|
self.get_params["columns[1][columnControl][search][type]"] = "text"
|
||||||
|
self.get_params["length"] = "20"
|
||||||
|
self.client.force_login(self.user)
|
||||||
|
request = self.factory.get('/fake-url/', data=self.get_params)
|
||||||
|
response = TestView()
|
||||||
|
response.setup(request)
|
||||||
|
data = json.loads(response.get(request).content)["data"]
|
||||||
|
self.assertEqual(len(data), 9)
|
||||||
|
|
||||||
|
def test_view_cc_1_search_equal(self):
|
||||||
|
self.get_params["columns[1][columnControl][search][value]"] = "1001 - Test Character - 1001"
|
||||||
|
self.get_params["columns[1][columnControl][search][logic]"] = "equal"
|
||||||
|
self.get_params["columns[1][columnControl][search][type]"] = "text"
|
||||||
|
self.get_params["length"] = "20"
|
||||||
|
self.client.force_login(self.user)
|
||||||
|
request = self.factory.get('/fake-url/', data=self.get_params)
|
||||||
|
response = TestView()
|
||||||
|
response.setup(request)
|
||||||
|
data = json.loads(response.get(request).content)["data"]
|
||||||
|
self.assertEqual(len(data), 1)
|
||||||
|
|
||||||
|
def test_view_cc_1_search_not_equal(self):
|
||||||
|
self.get_params["columns[1][columnControl][search][value]"] = "1001 - Test Character - 1001"
|
||||||
|
self.get_params["columns[1][columnControl][search][logic]"] = "notEqual"
|
||||||
|
self.get_params["columns[1][columnControl][search][type]"] = "text"
|
||||||
|
self.get_params["length"] = "20"
|
||||||
|
self.client.force_login(self.user)
|
||||||
|
request = self.factory.get('/fake-url/', data=self.get_params)
|
||||||
|
response = TestView()
|
||||||
|
response.setup(request)
|
||||||
|
data = json.loads(response.get(request).content)["data"]
|
||||||
|
self.assertEqual(len(data), 19)
|
||||||
|
|||||||
@@ -52,58 +52,42 @@ Given the `EveCharacter` Model as our model of choice we would define our stubs
|
|||||||
<th>{% translate "Alliance" %}</th>
|
<th>{% translate "Alliance" %}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</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>
|
</table>
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
{% block extra_css %}
|
{% block extra_css %}
|
||||||
{% include 'bundles/datatables-css-bs5.html' %}
|
<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 %}
|
{% endblock %}
|
||||||
{% block extra_javascript %}
|
{% block extra_javascript %}
|
||||||
{% include 'bundles/datatables-js-bs5.html' %}
|
<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>
|
<script>
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
$('#table').DataTable({
|
$('#table').DataTable({
|
||||||
serverSide: true,
|
serverSide: true,
|
||||||
ajax: '{% url "appname:table" %}',
|
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: [
|
columnDefs: [
|
||||||
{ "searchable": false, "targets": [0] },
|
{
|
||||||
{ "sortable": false, "targets": [0] },
|
targets: [0],
|
||||||
|
columnControl: [],
|
||||||
|
sortable: false,
|
||||||
|
searchable: false
|
||||||
|
|
||||||
|
},
|
||||||
|
{
|
||||||
|
targets: [1,2,3],
|
||||||
|
columnControl: [
|
||||||
|
{
|
||||||
|
target: 0,
|
||||||
|
content: []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
target: 1,
|
||||||
|
content: ['search']
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
],
|
],
|
||||||
order: [
|
order: [
|
||||||
[1, "asc"]
|
[1, "asc"]
|
||||||
|
|||||||
Reference in New Issue
Block a user