sangue/django/contatti_app/drilldown_autocomplete.py

133 lines
5.0 KiB
Python

import json
from functools import update_wrapper
from django.apps import apps
from django.contrib import admin
from django.contrib.admin.views.autocomplete import AutocompleteJsonView
from django.contrib.admin.widgets import AutocompleteMixin, AutocompleteSelect
from django.core.exceptions import FieldDoesNotExist, PermissionDenied
from django.db.models import Case, Q, Value, When
from django.http import Http404, JsonResponse
from django.urls import path
class DrillDownAutocompleteJsonView(AutocompleteJsonView):
"""Handle AutocompleteWidget's AJAX requests for data."""
def process_request(self, request):
"""
Validate request integrity, extract and return request parameters.
Since the subsequent view permission check requires the target model
admin, which is determined here, raise PermissionDenied if the
requested app, model or field are malformed.
Raise Http404 if the target model admin is not configured properly with
search_fields.
"""
(
term,
model_admin,
source_field,
to_field_name
) = super().process_request(request)
linkedfields = request.GET.get("linkedfields")
if linkedfields:
try:
linkedfields = json.loads(linkedfields)
except json.decoder.JSONDecodeError as e:
raise PermissionDenied from e
app_label = request.GET["app_label"]
model_name = request.GET["model_name"]
source_model = apps.get_model(app_label, model_name)
try:
drilldown_field = self.admin_site._registry[
source_model].get_drilldown_autocomplete_fields(request)[source_field.name]
except KeyError as e:
raise PermissionDenied from e
remote_model = source_field.remote_field.model
try:
for v in drilldown_field['linked'].values():
remote_model._meta.get_field(v)
except FieldDoesNotExist as e:
raise PermissionDenied from e
self.remote_model = remote_model
self.linkedfields = linkedfields
self.drilldown_filter_data = {
v: linkedfields[k] for k, v in drilldown_field['linked'].items() if k in linkedfields}
return term, model_admin, source_field, to_field_name
def get_queryset(self):
"""Return queryset based on ModelAdmin.get_search_results()."""
qs = super().get_queryset().only()
# print('Prima:',qs.query,'\n\n')
if hasattr(self, 'linkedfields'):
drilldown_filter_conditions = Q(**self.drilldown_filter_data)
qs = qs.annotate(ddok=Case(When(drilldown_filter_conditions, then=Value(
1)), default=Value(0))).order_by('-ddok', *qs.query.order_by)
# print('Dopo:',qs.query,'\n\n')
else:
qs = qs.annotate(ddok=Value(1))
return qs
def serialize_result(self, obj, to_field_name):
"""
Convert the provided model object to a dictionary that is added to the
results list.
"""
return {"id": str(getattr(obj, to_field_name)), "text": str(obj), "ddok": obj.ddok}
class DrillDownAutocompleteMixin(AutocompleteMixin):
url_name = "%s:drilldown_autocomplete"
class DrillDownAutocompleteSelect(AutocompleteSelect, DrillDownAutocompleteMixin):
pass
class DrillDownAutocompleteModelAdmin(admin.options.BaseModelAdmin):
drilldown_autocomplete_fields = dict()
class Media:
css = {
'all': ('admin/css/drilldown_autocomplete.css',)
}
def get_drilldown_autocomplete_fields(self, request):
return self.drilldown_autocomplete_fields
def formfield_for_foreignkey(self, db_field, request, **kwargs):
"""
Get a form Field for a ForeignKey.
"""
db = kwargs.get("using")
if "widget" not in kwargs:
daf = self.get_drilldown_autocomplete_fields(request)
if db_field.name in daf:
kwargs["widget"] = DrillDownAutocompleteSelect(
db_field, self.admin_site, attrs={
"data-linkedfields": json.dumps(list(daf[db_field.name]['linked'].keys())),
}, using=db
)
return super().formfield_for_foreignkey(db_field, request, **kwargs)
def drilldown_autocomplete_view(self, request):
return DrillDownAutocompleteJsonView.as_view(admin_site=self.admin_site)(request)
def get_urls(self):
def wrap(view, cacheable=False):
def wrapper(*args, **kwargs):
return self.admin_site.admin_view(view, cacheable)(*args, **kwargs)
wrapper.admin_site = self
return update_wrapper(wrapper, view)
urls = super().get_urls()
drilldown_urls = [
path("drilldown/", wrap(self.drilldown_autocomplete_view),
name="drilldown_autocomplete"),
]
return drilldown_urls + urls