Условное заполнение выпадающего списка в бэкенде Django с помощью list_filters
Мой models.py
:
from smart_selects.db_fields import ChainedForeignKey
class Funder(models.Model):
name = models.CharField(max_length=200)
scheme = models.ManyToManyField('Scheme', blank=True)
class Scheme(models.Model):
name = models.CharField(max_length=200)
class Project(models.Model):
funder = models.ForeignKey(Funder, on_delete=models.PROTECT)
scheme = ChainedForeignKey(
Scheme,
chained_field="funder",
chained_model_field="funder",
show_all=False,
auto_choose=True,
sort=True, null=True, blank=True)
Как вы можете видеть, я уже использую smart-selects для получения только схем, которые принадлежат данному конкретному фонду, доступных в Project
выпадающем выборе в бэкенде администратора. smart-selects
, однако, не заботится о том, что происходит в list_filters
разделе админки.
Что я хотел бы достичь: имею два "цепных" выпадающих фильтра в моей таблице Project admin, где я фильтрую проекты, которые имеют определенного спонсора, и как только я отфильтровал такие проекты, я хочу видеть только scheme
, которые принадлежат этому конкретному funder
в выпадающем scheme
s, с возможностью более тщательной фильтрации проектов с этой конкретной схемой.
Моя неудачная попытка (admin/py
):
from admin_auto_filters.filters import AutocompleteFilter, AutocompleteFilterFactory
class SchemeFilter(
AutocompleteFilterFactory(
'new Scheme', 'scheme'
)
):
def lookups(self, request, model_admin):
funder = request.GET.get('funder__pk__exact', '')
if funder:
schemes= Scheme.objects.filter(funder__exact=funder).distinct()
else:
schemes = Scheme.objects.none()
return schemes
def queryset(self, request, queryset):
funder = request.GET.get('funder__pk__exact', '')
if funder and Scheme.objects.filter(funder__exact=funder).count():
return queryset.filter(scheme__exact=self.value())
return queryset
class FunderFilter(AutocompleteFilter):
title = 'Funder'
field_name = 'funder'
class ProjectAdmin(NumericFilterModelAdmin, ImportExportModelAdmin):
search_fields = ['title', 'project_number']
list_filter = [FunderFilter, SchemeFilter,]
[...]
Возможно, вы сможете сделать это, переопределив некоторые методы в классе ModelAdmin.
enter code here class ProjectAdmin(admin.ModelAdmin):
list_filter = ('funder', 'scheme')
def get_changelist_instance(self, request):
changelist_instance = super().get_changelist_instance(request)
funder_filter = changelist_instance.get_filters(request)[0][0]
funder_id = funder_filter.used_parameters.get('funder', None)
if funder_id:
self.list_filter = (
('funder', admin.RelatedOnlyFieldListFilter),
('scheme', admin.RelatedOnlyFieldListFilter),
)
else:
self.list_filter = ('funder',)
return changelist_instance
В этом коде переопределите метод get_changelist_instance ModelAdmin. Вызовите метод get_changelist_instance родителя, чтобы получить экземпляр ChangeList. Затем получите фильтр funder и проверьте, был ли он использован (т. е. выбрал ли пользователь funder). Если он был выбран, мы устанавливаем атрибут list_filter таким образом, чтобы он включал фильтры funder и scheme. Если ни один из спонсоров не был выбран, мы включаем только фильтр спонсора.
Таким образом, фильтр схем появится только в том случае, если выбран спонсор, и в него будут включены только схемы, связанные с выбранным спонсором.