Как показать всплывающее окно связанных объектов, когда пользователь нажимает на выбранные параметры поля автозаполнения в Django admin? - PullRequest
0 голосов
/ 26 апреля 2020

Я хочу показать стандартное всплывающее окно с похожим объектом, когда пользователь нажимает выбранную опцию в поле множественного выбора с автозаполнением администратора Django, как это работает при нажатии на иконку карандаша поля ForeignKey *.

Модели следующие:

class Author(models.Model):
    name = models.CharField(_('name'), max_length=160)

class Book(models.Model):
    authors = models.ManyToManyField(Author, verbose_name=_('authors'), blank=True)
    ...

Возможно ли сделать это, расширив Django admin?

1 Ответ

1 голос
/ 03 мая 2020

Я обнаружил, что добавить требуемый пользовательский 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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...