Как использовать цепочку селектов django_select2 в панели администратора django?

Я взял почти неизменное руководство из документации есть мой models.py

from django.db import models


class Country(models.Model):
    name = models.CharField(max_length=255)


class City(models.Model):
    name = models.CharField(max_length=255)
    country = models.ForeignKey('Country', related_name="cities", on_delete=models.CASCADE)


class Address(models.Model):
    country = models.ForeignKey('Country', on_delete=models.CASCADE)
    city = models.ForeignKey('City', on_delete=models.CASCADE)

и создали ModelForm, потому что простая форма из документации не подходит для панели администратора

class AddressForm(forms.ModelForm):
    class Meta:
        model = Address
        fields = '__all__'
        widgets = {
            'country': ModelSelect2Widget(
                model=Country,
                search_fields=['name__icontains'],
            ),
            'city': ModelSelect2Widget(
                model=City,
                search_fields=['name__icontains'],
                dependent_fields={'country': 'country'},
                max_results=500,
            )
        }

на простой, не админской, странице он работает нормально. Я попробовал это только для тестирования. Мне действительно нужно только в админ панели. Вот шаблон из документации

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Create Book</title>
    {{ form.media.css }}
    <style>
        input, select {width: 100%}
    </style>
</head>
<body>
    <h1>Create a new Book</h1>
    <form method="POST">
        {% csrf_token %}
        {{ form.as_p }}
        <input type="submit">
    </form>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    {{ form.media.js }}
</body>
</html>

и класс представления

class AddressCreateView(generic.CreateView):
    model = models.Address
    form_class = forms.AddressForm
    success_url = "/"
    template_name = 'address.html'

Но в панели администратора эта же форма не работает. Selects не содержит никаких элементов и нет поля поиска. также нет никаких запросов, отправленных на сервер, которые я могу видеть при взаимодействии с selects на простой странице

from django.contrib import admin
from .models import City, Country, Address
from .forms import AddressForm


@admin.register(Address)
class AddressAdmin(admin.ModelAdmin):
    class Media:
        js = (
            'https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js',
        )

    form = AddressForm


admin.site.register(City)
admin.site.register(Country)

Этот ответ сэкономит вам много времени, НЕ используйте 'django_select2' для реализации цепочки полей select в админке Django, вместо этого используйте Django AutoComplete Light с Django-Hacker. Где DAL будет отвечать за реализацию AJAX запросов для получения данных для цепочки Select2 полей, а Django-Hacker сэкономит ваше время, настраивая стандартные формы Django непосредственно из любого места в api без необходимости создавать специальные формы для вашего класса администратора.

ШАГ1: установите указанные пакеты, а затем добавьте следующие пакеты сверху INSTALLED_APPS в settings.py файл:

settings.py

INSTALLED_APPS = [
'dal',
'dal_select2',
]

Примечание: это обязательно отменит скрипт jquery.init.js, предоставленный администратором, который устанавливает jQuery с помощью noConflict, делая jQuery доступным только в django.jQuery, а не $ (глобально).

ШАГ2: Убедитесь, что вы зарегистрировали ваш api urls.py файл в главном urls.py файле вашего Django проекта.

ШАГ 3: Создайте класс представления, который будет использоваться для получения и обновления данных (с помощью AJAX) для вашего цепного поля Select2 на странице администратора, как показано в примере ниже.

views.py

from dal import autocomplete

from your_api.models import model_name, depend_on_model


class MyAutocompleteView(autocomplete.Select2QuerySetView):
    """Select2 autocomplete view class to return queryset for chained Select2 field
    on admin page depending on specific field value"""

    def get_queryset(self):
        """Customize the queryset for this class view"""

        # Note: Don't forget to filter out results depending on the visitor !

        # Make sure the HTTP request is made by authenticated user, if 
        # unauthenticated then return nothing.
        if not self.request.user.is_authenticated:
            return model_name.objects.none()

        value = self.forwarded.get('query_string_name', None)

        # Check if HTTP request have a value for the query string (URL parameter).
        if value:
            queryset = depend_on_model.objects.filter(
                depend_on_field=value
            ).order_by('-created_at')
    
            return queryset
    

ШАГ 4: Наконец, вам нужно зарегистрировать это представление в вашем api urls.py файле и в то же время создать поле формы для вашего цепного поля Select2.

urls.py


from django.urls import path
from django import forms

from dal import autocomplete

import djhacker

from your_api import views
from your_api.models import model_name

# Define a variable that help to identify which api that creating URL from when
# using reverse function.
app_name = 'your_api'

urlpatterns = [
    path(
        'my-autocomplete/',
        views.MyAutocompleteView.as_view(),
        name='my-autocomplete',
    ),
]

# Now hack your model field to always render the autocomplete field with
# that view!
djhacker.formfield(
    # Specify the field of the model.
    model_name.field_name_to_be_chained_selec2,
    # Specify the form field type, since our field is a Foreign key, so we set
    # it to be choice field.
    forms.ModelChoiceField,
    # Set the widget for this form field.
    widget=autocomplete.ModelSelect2(
        # Set your django api url:
        # '<api name>:<name of the registered view path>'
        #
        # Note: make sure you registered the api routes in your main 'urls.py' file.
        url='your_api:my-autocomplete',
        # Set the depend on field in order to be forward its value to the view.
        forward=['depend_on_field'],
        # You can set some options for this Select2 field.
        attrs={
            # Set placeholder
            'data-placeholder': 'Autocomplete...',
            },
        )
)

Вот и все, мы просто сделали цепочку Select2, которая зависит от значения другого поля, и вы можете проверить его на странице администратора Django для связанной модели без необходимости создавать сложный настраиваемый ajax файл, настраивать ваш Django admin JavaScript или добавлять кэш-сервер... и т.д.

Вернуться на верх