Features: 1) Add RelatedAutocompleteFilter for admin filters with autocomplete support; 2) Introduce autocomplete_list_filter.html template for the new filter; 3) Enable dynamic filtering with client-side autocomplete functionality;
Fixes: 1) Add missing imports for `FieldListFilter` and `reverse` in `core/admin.py`; Extra: 1) Include relevant JS and CSS assets for autocomplete functionality; 2) Add comments and structure for better readability in template and script.
This commit is contained in:
parent
a63aa0371a
commit
c5397c6608
2 changed files with 89 additions and 5 deletions
|
|
@ -3,9 +3,10 @@ from contextlib import suppress
|
||||||
from constance.admin import Config
|
from constance.admin import Config
|
||||||
from constance.admin import ConstanceAdmin as BaseConstanceAdmin
|
from constance.admin import ConstanceAdmin as BaseConstanceAdmin
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
from django.contrib.admin import ModelAdmin, TabularInline, action, register, site
|
from django.contrib.admin import FieldListFilter, ModelAdmin, TabularInline, action, register, site
|
||||||
from django.contrib.gis.admin import GISModelAdmin
|
from django.contrib.gis.admin import GISModelAdmin
|
||||||
from django.db.models import Model
|
from django.db.models import Model
|
||||||
|
from django.urls import reverse
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from modeltranslation.translator import NotRegistered, translator
|
from modeltranslation.translator import NotRegistered, translator
|
||||||
from modeltranslation.utils import get_translation_fields
|
from modeltranslation.utils import get_translation_fields
|
||||||
|
|
@ -36,6 +37,49 @@ from .models import (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class RelatedAutocompleteFilter(FieldListFilter):
|
||||||
|
template = "admin/autocomplete_list_filter.html"
|
||||||
|
limit = 10
|
||||||
|
|
||||||
|
def __init__(self, field, request, params, model, model_admin, field_path):
|
||||||
|
self.field = field
|
||||||
|
self.request = request
|
||||||
|
self.params = params
|
||||||
|
self.model_admin = model_admin
|
||||||
|
self.field_path = field_path
|
||||||
|
|
||||||
|
self.lookup_kwarg = f"{field_path}__{field.target_field.name}__exact"
|
||||||
|
self.lookup_val = params.get(self.lookup_kwarg)
|
||||||
|
|
||||||
|
rel_model = field.remote_field.model
|
||||||
|
app_label = rel_model._meta.app_label
|
||||||
|
model_name = rel_model._meta.model_name
|
||||||
|
url_name = f"admin:{app_label}_{model_name}_autocomplete"
|
||||||
|
self.autocomplete_url = (
|
||||||
|
reverse(url_name)
|
||||||
|
+ f"?field_name={field.name}&limit={self.limit}"
|
||||||
|
)
|
||||||
|
|
||||||
|
def expected_parameters(self):
|
||||||
|
return [self.lookup_kwarg]
|
||||||
|
|
||||||
|
def has_output(self):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def choices(self, changelist):
|
||||||
|
yield {
|
||||||
|
"selected": bool(self.lookup_val),
|
||||||
|
"query_string": changelist.get_query_string(
|
||||||
|
{self.lookup_kwarg: self.lookup_val} if self.lookup_val else {},
|
||||||
|
[]
|
||||||
|
),
|
||||||
|
"display": _("—"),
|
||||||
|
"autocomplete_url": self.autocomplete_url,
|
||||||
|
"lookup_kwarg": self.lookup_kwarg,
|
||||||
|
"lookup_val": self.lookup_val or "",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class FieldsetsMixin:
|
class FieldsetsMixin:
|
||||||
general_fields: list = []
|
general_fields: list = []
|
||||||
relation_fields: list = []
|
relation_fields: list = []
|
||||||
|
|
@ -242,16 +286,14 @@ class ProductAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
|
||||||
"rating",
|
"rating",
|
||||||
"modified",
|
"modified",
|
||||||
)
|
)
|
||||||
autocomplete_list_filter = (
|
|
||||||
"category",
|
|
||||||
"brand",
|
|
||||||
)
|
|
||||||
list_filter = (
|
list_filter = (
|
||||||
"is_active",
|
"is_active",
|
||||||
"is_digital",
|
"is_digital",
|
||||||
"stocks__vendor",
|
"stocks__vendor",
|
||||||
"created",
|
"created",
|
||||||
"modified",
|
"modified",
|
||||||
|
("brand", RelatedAutocompleteFilter),
|
||||||
|
("category", RelatedAutocompleteFilter),
|
||||||
)
|
)
|
||||||
search_fields = (
|
search_fields = (
|
||||||
"name",
|
"name",
|
||||||
|
|
|
||||||
42
core/templates/admin/autocomplete_list_filter.html
Normal file
42
core/templates/admin/autocomplete_list_filter.html
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
{% load i18n admin_urls static %}
|
||||||
|
<div class="filter-autocomplete">
|
||||||
|
{# A hidden input to carry the selected value #}
|
||||||
|
<input type="hidden" name="{{ spec.lookup_kwarg }}" value="{{ spec.lookup_val }}" id="id_{{ spec.lookup_kwarg }}"/>
|
||||||
|
{# The visible text input #}
|
||||||
|
<label for="id_autocomplete_{{ spec.lookup_kwarg }}"></label><input type="text"
|
||||||
|
placeholder="{% trans 'Search…' %}"
|
||||||
|
id="id_autocomplete_{{ spec.lookup_kwarg }}"
|
||||||
|
style="width: 90%;"
|
||||||
|
data-autocomplete-url="{{ spec.autocomplete_url }}"
|
||||||
|
value="{% if spec.lookup_val %}{{ spec.lookup_val|get_obj_display:spec.field_path }}{% endif %}"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="{% static 'admin/js/jquery.init.js' %}"></script>
|
||||||
|
<script src="{% static 'admin/js/jquery.ui.core.js' %}"></script>
|
||||||
|
<script src="{% static 'admin/js/jquery.ui.autocomplete.js' %}"></script>
|
||||||
|
<link rel="stylesheet" href="{% static 'admin/css/jquery-ui.css' %}"/>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(function ($) {
|
||||||
|
let $input = $('#id_autocomplete_{{ spec.lookup_kwarg }}');
|
||||||
|
let $hidden = $('#id_{{ spec.lookup_kwarg }}');
|
||||||
|
|
||||||
|
$input.autocomplete({
|
||||||
|
source: function (request, response) {
|
||||||
|
$.getJSON($input.data('autocomplete-url'), {
|
||||||
|
term: request.term // admin autocomplete expects "term"
|
||||||
|
}, response);
|
||||||
|
},
|
||||||
|
minLength: 2,
|
||||||
|
select: function (event, ui) {
|
||||||
|
// ui.item.id and ui.item.text come from the JSON returned
|
||||||
|
$hidden.val(ui.item.id);
|
||||||
|
// trigger a reload of the changelist with the new filter
|
||||||
|
let params = {};
|
||||||
|
params["{{ spec.lookup_kwarg }}"] = ui.item.id;
|
||||||
|
window.location.search = $.param(params);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})(django.jQuery);
|
||||||
|
</script>
|
||||||
Loading…
Reference in a new issue