This commit is contained in:
Aaron Kable
2026-01-08 08:50:54 +08:00
parent d4e3addd6c
commit 1c7c775029
2 changed files with 72 additions and 49 deletions

View File

@@ -12,54 +12,80 @@ from allianceauth.services.hooks import get_extension_logger
logger = get_extension_logger(__name__) logger = get_extension_logger(__name__)
class DataTablesView(View): class DataTablesView(View):
"""
Basic DataTables server side table rendering
"""
model: Model = None model: Model = None
templates: list[str] = []
columns: list[tuple] = [] columns: list[tuple] = []
COL_SEARCH_REGEX = r"columns\[(?P<id>[0-9]{1,})\]\[search\]\[value\]"
COL_SEARCHABLE_REGEX = r"columns\[(?P<id>[0-9]{1,})\]\[searchable\]"
COL_SEARCHABLE_AS_REGEX_REGEX = r"columns\[(?P<id>[0-9]{1,})\]\[search\]\[regex\]"
COL_ORDERABLE_REGEX = r"columns\[(?P<id>[0-9]{1,})\]\[orderable\]"
search_regex: re.Pattern = None
searchable_regex: re.Pattern = None
searchable_as_regex_regex: re.Pattern = None
order_regex: re.Pattern = None
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.search_regex = re.compile(self.COL_SEARCH_REGEX)
self.searchable_regex = re.compile(self.COL_SEARCHABLE_REGEX)
self.searchable_as_regex_regex = re.compile(self.COL_SEARCHABLE_AS_REGEX_REGEX)
self.order_regex = re.compile(self.COL_ORDERABLE_REGEX)
def get_model_qs(self, request: HttpRequest): def get_model_qs(self, request: HttpRequest):
return self.model.objects return self.model.objects
def get_param(self, request: HttpRequest, key: str, cast=str, default=""): def get_param(self, request: HttpRequest, key: str, cast=str, default=""):
return cast(request.GET.get(key, default)) return cast(request.GET.get(key, default))
def filter_qs(self, search_string: str): def filter_qs(self, search_string: str, column_conf: dict):
# Global Search # Search
filter_q = Q() filter_q = Q()
if len(search_string) > 0: for id, c in column_conf.items():
val = search_string _c = self.columns[id][0]
for x in self.columns: if c["searchable"] and len(_c) > 0:
if x[0]: if len(c["search"]) and len(_c):
# Apply search to all Columns if c["regex"]:
filter_q |= Q(**{f'{x[1]}__icontains': val}) filter_q |= Q(**{f'{_c}__iregex': c["search"]})
else:
filter_q |= Q(**{f'{_c}__icontains': c["search"]})
if len(search_string) > 0:
filter_q |= Q(**{f'{_c}__icontains': search_string})
return filter_q return filter_q
def filter_col_qs(self, get: dict): def get_columns_config(self, get: dict):
# Row Search columns = defaultdict(
# TODO check if we have a regex search or not lambda: {
# TODO check if searchable or not "searchable": False,
col_id_regex = r"columns\[(?P<id>[0-9]{1,})\]\[search\]\[value\]" "orderable": False,
regex = re.compile(col_id_regex) "regex": False,
filter_q = Q() "search": ""
}
)
for c in get: for c in get:
_r = regex.findall(c) _r = self.search_regex.findall(c)
if _r: if _r:
_c = self.columns[int(_r[0])] columns[int(_r[0])]["search"] = get[c]
if _c[0]: _r_s = self.searchable_regex.findall(c)
if len(get[c]): if _r_s:
filter_q |= Q(**{f'{_c[1]}__iregex': get[c]}) columns[int(_r_s[0])]["searchable"] = get[c] == "true"
return filter_q _r_s_r = self.searchable_as_regex_regex.findall(c)
if _r_s_r:
columns[int(_r_s_r[0])]["regex"] = get[c] == "true"
_r_o = self.order_regex.findall(c)
if _r_o:
columns[int(_r_o[0])]["orderable"] = get[c] == "true"
return columns
def order_str(self, order_col, order_dir): def order_str(self, order_col, order_dir, column_conf: dict):
order = "" order = ""
_o = self.columns[order_col] _o = column_conf[order_col]
if _o[0]: if _o["orderable"]:
if order_dir == 'desc': if order_dir == 'desc':
order = '-' + _o[1] order = '-' + self.columns[order_col][0]
else: else:
order = _o[1] order = self.columns[order_col][0]
return order return order
def render_template(self, request: HttpRequest, template: str, ctx: dict): def render_template(self, request: HttpRequest, template: str, ctx: dict):
@@ -85,24 +111,26 @@ class DataTablesView(View):
limit = start + length limit = start + length
column_conf = self.get_columns_config(request.GET)
# Searches # Searches
filter_q = Q() | self.filter_qs(search_string) | self.filter_col_qs(request.GET) filter_q = Q() | self.filter_qs(search_string, column_conf)
# Build response rows # Build response rows
items = [] items = []
qs = self.get_model_qs(request).filter(filter_q).order_by() qs = self.get_model_qs(request).filter(filter_q).order_by()
# Apply ordering # Apply ordering
order = self.order_str(order_col, order_dir) order = self.order_str(order_col, order_dir, column_conf)
if order != "": if order != "":
qs = qs.order_by(order) qs = qs.order_by(order)
# build output # build output
for row in qs[start:limit]: for row in qs[start:limit]:
ctx = {"row": row} ctx = {"note": row}
row = [] row = []
for t in self.templates: for t in self.columns:
row.append(self.render_template(request, t, ctx)) row.append(self.render_template(request, t[1], ctx))
items.append(row) items.append(row)
# Build our output dict # Build our output dict

View File

@@ -130,22 +130,17 @@ from allianceauth.eveonline.models import EveCharacter
## Datatables server side view ## Datatables server side view
class EveCharacterTable(DataTablesView): class EveCharacterTable(DataTablesView):
model = EveCharacter model = EveCharacter
# Columns are rendered from these templates
# Templates can be a html file or template language directly in the list below
templates = [
"appname/stubs/icon.html",
"appname/stubs/name.html",
"appname/stubs/corp.html",
"{{ row.alliance_name }} {{ row.alliance_id }}"
]
# Define the columns for filtering and ordering # 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 = [ columns = [
# (can_sort: bool, "field_for_queries_or_sort") # ("field_for_queries_or_sort", template: str)
(False, ""), ("", "appname/stubs/icon.html"),
(True, "character_name"), ("character_name", "appname/stubs/name.html"),
(True, "corporation_name"), ("corporation_name", "appname/stubs/corp.html"),
(True, "alliance_name"), ("alliance_name", "{{ row.alliance_name }} {{ row.alliance_id }}"),
] ]
# if you need to do some prefetch or pre-filtering you can overide this function # if you need to do some prefetch or pre-filtering you can overide this function