Features: 1) Remove AutocompleteFieldListFilter and related admin filters and templates; 2) Remove associated JavaScript, CSS, and Python logic; 3) Simplify admin configurations by dropping unused autocomplete filters;

Fixes: None;

Extra: 1) Cleanup unused imports and redundant UI files; 2) Minor adjustments to filter handling in `core/admin.py`.
This commit is contained in:
Egor Pavlovich Gorbunov 2025-07-01 20:20:50 +03:00
parent 8a07fc69b1
commit ce53eec560
5 changed files with 1 additions and 253 deletions

View file

@ -1,4 +1,3 @@
from contextlib import suppress from contextlib import suppress
from constance.admin import Config from constance.admin import Config
@ -12,7 +11,6 @@ from modeltranslation.translator import NotRegistered, translator
from modeltranslation.utils import get_translation_fields from modeltranslation.utils import get_translation_fields
from mptt.admin import DraggableMPTTAdmin from mptt.admin import DraggableMPTTAdmin
from core.filters import AutocompleteFieldListFilter
from core.forms import OrderForm, OrderProductForm, VendorForm from core.forms import OrderForm, OrderProductForm, VendorForm
from core.models import ( from core.models import (
Address, Address,
@ -249,8 +247,6 @@ class ProductAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
"stocks__vendor", "stocks__vendor",
"created", "created",
"modified", "modified",
("brand", AutocompleteFieldListFilter),
("category", AutocompleteFieldListFilter),
) )
search_fields = ( search_fields = (
"name", "name",

View file

@ -2,8 +2,7 @@ import json
import logging import logging
import uuid import uuid
from django.contrib.admin import FieldListFilter from django.core.exceptions import BadRequest
from django.core.exceptions import BadRequest, ValidationError
from django.db.models import ( from django.db.models import (
Avg, Avg,
Case, Case,
@ -17,7 +16,6 @@ from django.db.models import (
When, When,
) )
from django.db.models.functions import Coalesce from django.db.models.functions import Coalesce
from django.forms import Media
from django.utils.http import urlsafe_base64_decode from django.utils.http import urlsafe_base64_decode
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django_filters import ( from django_filters import (
@ -505,78 +503,3 @@ class AddressFilter(FilterSet):
class Meta: class Meta:
model = Address model = Address
fields = ["uuid", "user_uuid", "user_email", "order_by"] fields = ["uuid", "user_uuid", "user_email", "order_by"]
class AutocompleteFieldListFilter(FieldListFilter):
template = "admin/autocomplete_filter.html"
def __init__(self, field, request, params, model, model_admin, field_path):
self.lookup_kwarg = f"{field_path}__exact"
self.lookup_val = request.GET.get(self.lookup_kwarg)
self.field = field
self.field_path = field_path
self.model = model
self.related_model = field.related_model
super().__init__(field, request, params, model, model_admin, field_path)
def has_output(self):
return True
def choices(self, changelist):
choices = []
if self.lookup_val:
try:
obj = self.related_model.objects.get(pk=self.lookup_val)
choices.append(
{
"selected": True,
"query_string": changelist.get_query_string(remove=[self.lookup_kwarg]),
"display": str(obj),
"value": obj.pk,
}
)
except (self.related_model.DoesNotExist, ValidationError):
pass
return choices
def queryset(self, request, queryset):
if self.lookup_val:
try:
return queryset.filter(**{self.lookup_kwarg: self.lookup_val})
except (ValueError, ValidationError):
pass
return queryset
def expected_parameters(self):
return [self.lookup_kwarg]
@property
def media(self):
extra = "" if self.used_parameters else ".min"
js = [
f"admin/js/vendor/jquery/jquery{extra}.js",
f"admin/js/vendor/select2/select2.full{extra}.js",
"admin/js/autocomplete_filter.js",
]
css = {
"screen": [
f"admin/css/vendor/select2/select2{extra}.css",
"admin/css/autocomplete_filter.css",
]
}
return Media(js=js, css=css)
def get_context_data(self):
field_name = self.field_path.replace("__", "_")
related_url = f'/admin/{self.related_model._meta.app_label}/{self.related_model._meta.model_name}/autocomplete/'
return {
'field_name': field_name,
'lookup_kwarg': self.lookup_kwarg,
'lookup_val': self.lookup_val or '',
'related_url': related_url,
'placeholder': _('Search %s...') % self.related_model._meta.verbose_name,
'clear_text': _('Clear'),
'field_verbose_name': self.field.verbose_name or self.field.name.replace('_', ' ').title(),
}

View file

@ -1,61 +0,0 @@
.autocomplete-filter-widget {
margin-bottom: 10px;
padding: 8px;
border: 1px solid #ddd;
background: #f9f9f9;
border-radius: 4px;
}
.autocomplete-filter-widget h3 {
margin: 0 0 8px 0;
font-size: 11px;
text-transform: uppercase;
font-weight: bold;
color: #666;
}
.autocomplete-filter-controls {
display: flex;
align-items: center;
gap: 8px;
}
.autocomplete-input {
flex: 1;
}
.autocomplete-clear {
background: #ba2121;
color: white;
border: none;
padding: 4px 8px;
border-radius: 3px;
cursor: pointer;
font-size: 11px;
text-decoration: none;
display: inline-block;
}
.autocomplete-clear:hover {
background: #a41e1e;
color: white;
}
.select2-container {
font-size: 12px;
}
.select2-container .select2-selection--single {
height: 28px;
border: 1px solid #ccc;
}
.select2-container .select2-selection--single .select2-selection__rendered {
line-height: 26px;
padding-left: 8px;
}
.select2-container .select2-selection--single .select2-selection__arrow {
height: 26px;
right: 4px;
}

View file

@ -1,86 +0,0 @@
(function ($) {
'use strict';
$(document).ready(function () {
$('.autocomplete-filter-widget').each(function () {
let $widget = $(this);
let $input = $widget.find('.autocomplete-input');
let $hiddenInput = $widget.find('.autocomplete-hidden');
let $clearBtn = $widget.find('.autocomplete-clear');
let relatedUrl = $input.data('related-url');
let placeholder = $input.attr('placeholder');
// Initialize Select2
$input.select2({
ajax: {
url: relatedUrl,
dataType: 'json',
delay: 250,
data: function (params) {
return {
term: params.term,
page: params.page || 1
};
},
processResults: function (data, params) {
params.page = params.page || 1;
return {
results: data.results.map(function (item) {
return {
id: item.id,
text: item.text
};
}),
pagination: {
more: data.pagination && data.pagination.more
}
};
},
cache: true
},
placeholder: placeholder,
allowClear: true,
minimumInputLength: 1,
width: '250px'
});
// Handle selection
$input.on('select2:select', function (e) {
let data = e.params.data;
$hiddenInput.val(data.id);
updateUrl();
});
// Handle clearing
$input.on('select2:clear', function (e) {
$hiddenInput.val('');
updateUrl();
});
// Clear button functionality
$clearBtn.on('click', function (e) {
e.preventDefault();
$input.val(null).trigger('change');
$hiddenInput.val('');
updateUrl();
});
function updateUrl() {
let currentUrl = new URL(window.location);
let lookupKwarg = $hiddenInput.data('lookup-kwarg');
let value = $hiddenInput.val();
if (value) {
currentUrl.searchParams.set(lookupKwarg, value);
} else {
currentUrl.searchParams.delete(lookupKwarg);
}
// Reset to first page when filtering
currentUrl.searchParams.delete('p');
window.location.href = currentUrl.toString();
}
});
});
})(django.jQuery);

View file

@ -1,24 +0,0 @@
{% load i18n %}
<div class="autocomplete-filter-widget">
<h3>{% trans "By" %} {{ field_verbose_name }}</h3>
<div class="autocomplete-filter-controls">
<select class="autocomplete-input"
data-related-url="{{ related_url }}"
placeholder="{{ placeholder }}">
{% if lookup_val %}
{% for choice in choices %}
{% if choice.selected %}
<option value="{{ choice.value }}" selected>{{ choice.display }}</option>
{% endif %}
{% endfor %}
{% endif %}
</select>
<input type="hidden"
class="autocomplete-hidden"
data-lookup-kwarg="{{ lookup_kwarg }}"
value="{{ lookup_val }}">
{% if lookup_val %}
<a href="#" class="autocomplete-clear">{{ clear_text }}</a>
{% endif %}
</div>
</div>