Features: 1) Replace autocomplete input with Select2-based dropdown for enhanced functionality and improved styling; 2) Add support for displaying the initial selection text in the dropdown; 3) Load additional Select2-specific JavaScript and CSS resources dynamically;
Fixes: 1) Correct handling of initial selection when lookup value is invalid or missing; Extra: 1) Refactor related admin template and Python code to improve readability and maintainability; 2) Align variable names and code style for consistency.
This commit is contained in:
parent
08dc89ef19
commit
306f54b4a2
2 changed files with 53 additions and 48 deletions
|
|
@ -44,20 +44,32 @@ class RelatedAutocompleteFilter(FieldListFilter):
|
||||||
def __init__(self, field, request, params, model, model_admin, field_path):
|
def __init__(self, field, request, params, model, model_admin, field_path):
|
||||||
super().__init__(field, request, params, model, model_admin, field_path)
|
super().__init__(field, request, params, model, model_admin, field_path)
|
||||||
self.lookup_kwarg = f"{field_path}__{field.target_field.name}__exact"
|
self.lookup_kwarg = f"{field_path}__{field.target_field.name}__exact"
|
||||||
self.lookup_val = params.get(self.lookup_kwarg, "")
|
self.lookup_val = params.get(self.lookup_kwarg, "")
|
||||||
|
|
||||||
|
remote = field.remote_field.model._meta
|
||||||
|
self.app_label = remote.app_label
|
||||||
|
self.model_name = remote.model_name
|
||||||
|
self.field_name = field.name
|
||||||
self.field_label = field.verbose_name
|
self.field_label = field.verbose_name
|
||||||
|
|
||||||
remote_opts = field.remote_field.model._meta
|
|
||||||
base = reverse("admin:autocomplete")
|
base = reverse("admin:autocomplete")
|
||||||
self.autocomplete_url = (
|
self.autocomplete_url = (
|
||||||
f"{base}"
|
f"{base}"
|
||||||
f"?app_label={remote_opts.app_label}"
|
f"?app_label={self.app_label}"
|
||||||
f"&model_name={remote_opts.model_name}"
|
f"&model_name={self.model_name}"
|
||||||
f"&field_name={field.name}"
|
f"&field_name={self.field_name}"
|
||||||
f"&limit={self.limit}"
|
f"&limit={self.limit}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if self.lookup_val:
|
||||||
|
try:
|
||||||
|
obj = field.remote_field.model._default_manager.get(pk=self.lookup_val)
|
||||||
|
self.initial_text = str(obj)
|
||||||
|
except field.remote_field.model.DoesNotExist:
|
||||||
|
self.initial_text = ""
|
||||||
|
else:
|
||||||
|
self.initial_text = ""
|
||||||
|
|
||||||
def expected_parameters(self):
|
def expected_parameters(self):
|
||||||
key = f"{self.field_path}__{self.field.target_field.name}__exact"
|
key = f"{self.field_path}__{self.field.target_field.name}__exact"
|
||||||
return [key]
|
return [key]
|
||||||
|
|
@ -67,15 +79,21 @@ class RelatedAutocompleteFilter(FieldListFilter):
|
||||||
|
|
||||||
def choices(self, changelist):
|
def choices(self, changelist):
|
||||||
yield {
|
yield {
|
||||||
"selected": bool(self.lookup_val),
|
"selected": bool(self.lookup_val),
|
||||||
"query_string": changelist.get_query_string(
|
"query_string": changelist.get_query_string(
|
||||||
({self.lookup_kwarg: self.lookup_val} if self.lookup_val else {}), []
|
({self.lookup_kwarg: self.lookup_val}
|
||||||
),
|
if self.lookup_val else {}),
|
||||||
"display": _("—"),
|
[]
|
||||||
|
),
|
||||||
|
"display": _("—"),
|
||||||
"autocomplete_url": self.autocomplete_url,
|
"autocomplete_url": self.autocomplete_url,
|
||||||
"lookup_kwarg": self.lookup_kwarg,
|
"lookup_kwarg": self.lookup_kwarg,
|
||||||
"lookup_val": self.lookup_val,
|
"lookup_val": self.lookup_val,
|
||||||
"field_label": self.field_label,
|
"app_label": self.app_label,
|
||||||
|
"model_name": self.model_name,
|
||||||
|
"field_name": self.field_name,
|
||||||
|
"field_label": self.field_label,
|
||||||
|
"initial_text": self.initial_text,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,40 +1,27 @@
|
||||||
{% load i18n static %}
|
{% load i18n static %}
|
||||||
<div class="filter-autocomplete">
|
<div class="filter-autocomplete">
|
||||||
<input type="hidden"
|
<label for="id_{{ spec.lookup_kwarg }}"></label><select name="{{ spec.lookup_kwarg }}"
|
||||||
name="{{ spec.lookup_kwarg }}"
|
id="id_{{ spec.lookup_kwarg }}"
|
||||||
value="{{ spec.lookup_val }}"
|
class="admin-autocomplete"
|
||||||
id="id_{{ spec.lookup_kwarg }}"/>
|
style="width:90%;"
|
||||||
|
data-ajax--url="{{ spec.autocomplete_url }}"
|
||||||
<label for="id_autocomplete_{{ spec.lookup_kwarg }}"></label><input type="text"
|
data-app-label="{{ spec.app_label }}"
|
||||||
id="id_autocomplete_{{ spec.lookup_kwarg }}"
|
data-model-name="{{ spec.model_name }}"
|
||||||
placeholder="{% blocktrans with label=spec.field_label %}Search by {{ label }}…{% endblocktrans %}"
|
data-field-name="{{ spec.field_name }}"
|
||||||
style="width:90%;"
|
data-allow-clear="true"
|
||||||
data-autocomplete-url="{{ spec.autocomplete_url }}"
|
data-placeholder="{% blocktrans with label=spec.field_label %}Search by {{ label }}…{% endblocktrans %}">
|
||||||
data-lookup-kwarg="{{ spec.lookup_kwarg }}"/>
|
{% if spec.lookup_val %}
|
||||||
|
<option value="{{ spec.lookup_val }}" selected>{{ spec.initial_text }}</option>
|
||||||
|
{% endif %}
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<link rel="stylesheet"
|
||||||
|
href="{% static 'admin/css/vendor/select2/select2.css' %}"/>
|
||||||
|
<link rel="stylesheet"
|
||||||
|
href="{% static 'admin/css/autocomplete.css' %}"/>
|
||||||
|
|
||||||
<script src="{% static 'admin/js/vendor/jquery/jquery.min.js' %}"></script>
|
<script src="{% static 'admin/js/vendor/jquery/jquery.min.js' %}"></script>
|
||||||
|
<script src="{% static 'admin/js/vendor/select2/select2.full.min.js' %}"></script>
|
||||||
<script>
|
<script src="{% static 'admin/js/jquery.init.js' %}"></script>
|
||||||
(function ($) {
|
<script src="{% static 'admin/js/autocomplete.js' %}"></script>
|
||||||
let $box = $('#id_autocomplete_{{ spec.lookup_kwarg }}');
|
|
||||||
let $hidden = $('#id_{{ spec.lookup_kwarg }}');
|
|
||||||
|
|
||||||
$box.autocomplete({
|
|
||||||
source: function (req, resp) {
|
|
||||||
$.getJSON($box.data('autocomplete-url'), {
|
|
||||||
term: req.term
|
|
||||||
}, function (data) {
|
|
||||||
resp(data.results);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
minLength: 2,
|
|
||||||
select: function (_, ui) {
|
|
||||||
$hidden.val(ui.item.id);
|
|
||||||
let qs = {};
|
|
||||||
qs[$box.data('lookup-kwarg')] = ui.item.id;
|
|
||||||
window.location.search = $.param(qs);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
})(django.jQuery);
|
|
||||||
</script>
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue