Ограничение вывода полей в админке Django в соответствии с предыдущим выбором
У меня есть этот model.py:
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):
title = models.CharField(max_length=200)
funder = models.ForeignKey(Funder)
scheme = models.ForeignKey(Scheme, on_delete=models.SET_NULL)
У фонда может быть 0, 1 или много scheme
, привязанных к нему. Я хотел бы ограничить выбор схем, которые можно выбрать из административной формы для project
, только теми scheme
, которые принадлежат конкретному фаундеру. Возможно ли это?
Пример:
в "новом проекте" люди выбирают финансиста1, а в выпадающем списке схем видят только схемы1, схемы3, схемы5, потому что схемы2 и схемы4 связаны с финансистом2.
Можно ли это получить с помощью QuerySet
?
Мой ModelAdmin
:
from import_export.admin import ImportExportModelAdmin
class FunderAdmin(ImportExportModelAdmin):
search_fields = ['name',]
autocomplete_fields = ['scheme']
list_display = ("name",)
Я могу порекомендовать smart-selects. Я могу сразу предупредить вас в settings.py
использовать USE_DJANGO_JQUERY = True
вместо: JQUERY_URL = True
. Я проверил ваши модели, поле выбора scheme
ограничено в зависимости от того, какой funder
выбран.
models.py
from smart_selects.db_fields import ChainedForeignKey
class Project(models.Model):
title = models.CharField(max_length=200)
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)
def __str__(self):
return self.title
urls.py
urlpatterns = [
path('admin/', admin.site.urls),
path(r'^chaining/', include('smart_selects.urls')),
]
Как вариант, можно передать данные в javascript function
и попытаться отфильтровать их. Но пока мне удалось только зарегистрировать обработку event
в виджете формы и получить выбранные funder
, но передать данные в javascript я не могу (на всякий случай привожу форму ниже, она не относится к первой рабочей версии):
class ProjectForm(forms.ModelForm):
class Meta:
model = Project
fields = '__all__'
widgets = {
'funder': forms.Select(attrs={'onchange': "Load();"})
}
Для достижения этого с текущей схемой, немного зависит от переопределения Django admin Поэтому я предлагаю решение, в котором вам просто нужно добавить маршрут и функцию выборки и немного переопределить Django admin js ( без каких-либо сторонних зависимостей).
Предположим, что у вас есть такие модели:
class Scheme(models.Model):
name = models.CharField(max_length=200)
def __str__(self):
return f"{self.id}: {self.name}"
class Funder(models.Model):
name = models.CharField(max_length=200)
scheme = models.ManyToManyField(Scheme, blank=True)
def __str__(self):
return f"{self.id}: {self.name}"
class Project(models.Model):
title = models.CharField(max_length=200)
funder = models.ForeignKey(Funder, on_delete=models.CASCADE)
scheme = models.ForeignKey(Scheme, on_delete=models.SET_NULL, null=True, blank=True)
def __str__(self):
return f"{self.id}: {self.title}"
Изменения в проекте admin.py будут выглядеть следующим образом
from django.contrib import admin
from import_export.admin import ImportExportModelAdmin
from funder.models import Funder
from .models import Project
from django.urls import path
from django.http import JsonResponse
class ProjectAdmin(ImportExportModelAdmin):
list_display = ("id", "title", "funder", "scheme")
list_filter = ("funder",)
search_fields = ("title",)
ordering = ("title",)
class Media:
# Include JavaScript files in the admin panel
js = ("js/admin_project.js",)
def get_urls(self):
urls = super(ProjectAdmin, self).get_urls()
custom_urls = [
path(
"fetch_schemes/",
self.fetch_schemes,
name="project_project_fetch_schemes",
),
]
return custom_urls + urls
def fetch_schemes(self, request):
funder_id = request.GET.get("funder_id")
if funder_id:
try:
funder = Funder.objects.get(id=funder_id)
schemes = list(funder.scheme.all().values("id", "name"))
return JsonResponse({"schemes": schemes})
except Funder.DoesNotExist:
return JsonResponse({"error": "Funder not found"})
else:
return JsonResponse({"error": "Funder ID not provided"})
admin.site.register(Project, ProjectAdmin)
Теперь просто добавьте js/admin_project.js в вашу папку static в Django
// admin_project.js
jQuery(document).ready(function($) {
// Store the current value of the scheme dropdown
var currentSchemeValue = $('#id_scheme').val();
$('#id_funder').change(function() {
var funderId = $(this).val();
if (funderId) {
$.ajax({
url: "/admin/project/project/fetch_schemes/?funder_id=" + funderId,
type: 'GET',
success: function (data) {
// Empty the scheme dropdown and add an empty option
$('#id_scheme').empty().append('<option value="">---</option>');
// Append schemes from the data
$.each(data.schemes, function (index, scheme) {
$('#id_scheme').append($('<option>', {
value: scheme.id,
text: `${scheme.id}:${scheme.name}`
}));
});
// Restore the previous value of the scheme dropdown, if it exists
if (currentSchemeValue) {
$('#id_scheme').val(currentSchemeValue);
}
}
});
} else {
// If no funder is selected, empty the scheme dropdown and add an empty option
$('#id_scheme').empty().append('<option value="">---</option>');
}
});
});
И вы готовы к работе.
Результаты/вывод:
Добавить проект без выбора спонсора
Добавьте поле схемы проекта при выборе спонсора
После сохранения списков проектов (включая пустой сценарий схемы)
Надеюсь, это поможет.
Счастливого кодирования!!!