Sith/core/views/widgets/select.py

112 lines
3.4 KiB
Python

from collections.abc import Collection
from typing import Any
from django.contrib.staticfiles.storage import staticfiles_storage
from django.db.models import Model, QuerySet
from django.forms import Select, SelectMultiple
from ninja import ModelSchema
from pydantic import TypeAdapter
from core.models import Group, SithFile, User
from core.schemas import GroupSchema, SithFileSchema, UserProfileSchema
class AutoCompleteSelectMixin:
component_name = "autocomplete-select"
template_name = "core/widgets/autocomplete_select.jinja"
model: type[Model] | None = None
adapter: TypeAdapter[Collection[ModelSchema]] | None = None
pk = "id"
js = [
"bundled/core/components/ajax-select-index.ts",
]
css = [
"bundled/core/components/ajax-select-index.css",
"core/components/ajax-select.scss",
]
def get_queryset(self, pks: Collection[Any]) -> QuerySet:
return self.model.objects.filter(
**{
f"{self.pk}__in": [
pk
for pk in pks
if str(pk).isdigit() # We filter empty values for create views
]
}
).all()
def __init__(self, attrs=None, choices=()):
if self.is_ajax:
choices = () # Avoid computing anything when in ajax mode
super().__init__(attrs=attrs, choices=choices)
@property
def is_ajax(self):
return self.adapter and self.model
def optgroups(self, name, value, attrs=None):
"""Don't create option groups when doing ajax"""
if self.is_ajax:
return []
return super().optgroups(name, value, attrs=attrs)
def get_context(self, name, value, attrs):
context = super().get_context(name, value, attrs)
context["widget"]["attrs"]["autocomplete"] = "off"
context["component"] = self.component_name
context["statics"] = {
"js": [staticfiles_storage.url(file) for file in self.js],
"css": [staticfiles_storage.url(file) for file in self.css],
}
if self.is_ajax:
context["initial"] = self.adapter.dump_json(
self.adapter.validate_python(
self.get_queryset(context["widget"]["value"])
)
).decode("utf-8")
return context
class AutoCompleteSelect(AutoCompleteSelectMixin, Select): ...
class AutoCompleteSelectMultiple(AutoCompleteSelectMixin, SelectMultiple): ...
class AutoCompleteSelectUser(AutoCompleteSelect):
component_name = "user-ajax-select"
model = User
adapter = TypeAdapter(list[UserProfileSchema])
class AutoCompleteSelectMultipleUser(AutoCompleteSelectMultiple):
component_name = "user-ajax-select"
model = User
adapter = TypeAdapter(list[UserProfileSchema])
class AutoCompleteSelectGroup(AutoCompleteSelect):
component_name = "group-ajax-select"
model = Group
adapter = TypeAdapter(list[GroupSchema])
class AutoCompleteSelectMultipleGroup(AutoCompleteSelectMultiple):
component_name = "group-ajax-select"
model = Group
adapter = TypeAdapter(list[GroupSchema])
class AutoCompleteSelectSithFile(AutoCompleteSelect):
component_name = "sith-file-ajax-select"
model = SithFile
adapter = TypeAdapter(list[SithFileSchema])
class AutoCompleteSelectMultipleSithFile(AutoCompleteSelectMultiple):
component_name = "sith-file-ajax-select"
model = SithFile
adapter = TypeAdapter(list[SithFileSchema])