Я обнаружил, что добавить требуемый пользовательский HTML проще с помощью виджета ModelSelect2Multiple
из django -autocomplete-light (DAL).
Конфигурация администратора выглядит следующим образом:
from dal import autocomplete
@admin.register(Book)
class BookAdmin(admin.ModelAdmin):
class Media:
js = ['/static/books/js/book-admin.js']
def formfield_for_manytomany(self, db_field, request, **kwargs):
if db_field.name == 'authors':
return forms.ModelMultipleChoiceField(
required=False,
label=Author._meta.verbose_name_plural.title(),
queryset=Author.objects.all(),
widget=autocomplete.ModelSelect2Multiple(
url='author-autocomplete',
attrs={'data-html': True}))
return super().formfield_for_foreignkey(db_field, request, **kwargs)
Представление DAL выглядит следующим образом:
from django.utils.safestring import mark_safe
from dal import autocomplete
from .models import Author
from django.urls import reverse
class AuthorAutocomplete(autocomplete.Select2QuerySetView):
def get_queryset(self):
if not self.request.user.is_staff:
return Author.objects.none()
qs = Author.objects.all()
if self.q:
qs = qs.filter(name__icontains=self.q)
return qs
def get_selected_result_label(self, item):
change_url = reverse('admin:books_author_change', kwargs={'object_id': item.id})
return mark_safe('<span onclick="event.stopPropagation(); showRelatedObjectPopup({'
f"href: '{change_url}?_popup=1', id: 'change_id_author'"
f'}})">{item.name}</span>')
При выборе нового автора в поле Авторы в Книга изменить представление в админке, элемент HTML управляется ModelSelect2Multiple
, поэтому пользовательский элемент HTML присутствует, и щелчок по вновь выбранному автору открывает всплывающее окно, как и предполагалось. Но пользовательский HTML не будет присутствовать в существующих выборках, поэтому обработчики кликов должны быть добавлены с jQuery в book-admin.js
:
'use strict';
window.addEventListener("load", function () {
/**
* Show related object popup when user clicks on selected author name.
*/
(function ($) {
var $authorsSelect2Selections = $('div.form-row.field-authors .select2-selection__choice > span:nth-child(2)');
var $authorOptions = $('#id_authors > option');
$authorsSelect2Selections.click(function ($event) {
$event.stopPropagation();
var self = this;
// Find corresponding option by text comparison, assuming that author name is unique
var $result = $authorOptions.filter(function() {
return $(this).text() === self.textContent;
});
showRelatedObjectPopup({
href: '/admin/books/author/' + $result.val() + '/change/?_popup=1',
id: 'change_id_other_authors'
});
});
})(django.jQuery);
});
event.stopPropagation()
предотвращает открытие раскрывающегося списка Select2.