我的
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, related_name='funders')
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
部分中发生的情况。
我想要实现的目标:在我的项目管理表中有两个“链接”下拉过滤器,我在其中过滤具有特定资助者的项目,一旦我过滤了此类项目,我只想查看
scheme
下拉列表中属于特定funder
的scheme
,能够进一步过滤具有该特定方案的项目。
到目前为止我的失败尝试(
admin/py
):
from admin_auto_filters.filters import AutocompleteFilter, AutocompleteFilterFactory
class SchemeFilter(
AutocompleteFilterFactory(
'new Scheme', 'scheme'
)
):
def lookups(self, request, model_admin):
this_funder = request.GET.get('funder__pk__exact', '')
if this_funder:
schemes= Scheme.objects.filter(funders__pk__exact=this_funder).distinct()
else:
schemes = Scheme.objects.none()
return schemes.values('pk', 'name')
def queryset(self, request, queryset):
this_funder = request.GET.get('funder__pk__exact', '')
if this_funder and Scheme.objects.filter(funders__pk__exact=this_funder).count():
return queryset.filter(funder__pk__exact=self.value())
return queryset.filter(scheme__pk__exact=self.value())
class FunderFilter(AutocompleteFilter):
title = 'Funder'
field_name = 'funder'
class ProjectAdmin(NumericFilterModelAdmin, ImportExportModelAdmin):
search_fields = ['title', 'project_number']
list_filter = [FunderFilter, SchemeFilter,]
[...]
更新我
我认为问题出在这里:
schemes= Scheme.objects.filter(funder__pk__exact=this_funder).distinct()
我现在添加了一个
related_name
。还是没有运气。
更新二
我意识到我正在编辑查询集,而我应该在
lookups
上工作。这是我更新的代码。我觉得我已经很接近找到解决方案了。看起来 lookup
if
被忽略,并且始终返回 scheme
的完整列表。
class SchemeFilter(
AutocompleteFilterFactory(
'Scheme', 'scheme'
)
):
def lookups(self, request, model_admin):
this_funder = request.GET.get('funder__pk__exact', '')
if (this_funder != '' and Scheme.objects.filter(funders__pk__exact=this_funder).count() > 0):
schemes= Scheme.objects.filter(funders__pk__exact=this_funder).distinct()
elif (this_funder != '' and Scheme.objects.filter(funders__pk__exact=this_funder).count() == 0):
schemes = Scheme.objects.none()
else:
schemes = Scheme.objects.all()
return schemes.values('pk', 'name')
def queryset(self, request, queryset):
if self.value():
queryset = queryset.filter(scheme__pk__exact=self.value()).distinct()
return super().queryset(request, queryset)
更新三
我开始认为这是
admin_auto_filters
的一个错误。事实上,如果我像这样使用原生 SimpleListFilter
:
class TestSchemeFilter(admin.SimpleListFilter):
title = 'Test Scheme'
parameter_name = 'scheme'
def lookups(self, request, model_admin):
pk = request.GET.get('funder__pk__exact', '')
if pk and Scheme.objects.filter(funders__pk__exact=pk).count() > 0:
schemes = Scheme.objects.filter(funders__id__exact=pk)
else:
schemes = Scheme.objects.all()
return [(s.id, s.name) for s in schemes]
def queryset(self, request, queryset):
if self.value():
queryset = queryset.filter(scheme__pk__exact=self.value()).distinct()
我成功获得了右侧仅属于该资助者的计划列表。
不幸的是,Django 的内置管理界面不支持这种级别的交互性。您可以考虑在管理界面之外创建一个自定义视图来处理这个特定的用例。
新的 Django 视图:
from django.shortcuts import render
from .models import Funder, Scheme, Project
def project_view(request):
funders = Funder.objects.all()
schemes = Scheme.objects.none()
projects = Project.objects.none()
if 'funder' in request.GET:
funder = Funder.objects.get(id=request.GET['funder'])
schemes = Scheme.objects.filter(funder=funder)
if 'scheme' in request.GET:
scheme = Scheme.objects.get(id=request.GET['scheme'])
projects = Project.objects.filter(funder=funder, scheme=scheme)
else:
projects = Project.objects.filter(funder=funder)
return render(request, 'project_view.html', {'funders': funders, 'schemes': schemes, 'projects': projects})
新的 Django 模板:
<form method="get">
<select name="funder" onchange="this.form.submit()">
<option value="">Select a funder</option>
{% for funder in funders %}
<option value="{{ funder.id }}"
{% if funder.id == request.GET.funder %}selected{% endif %}>
{{ funder.name }}
</option>
{% endfor %}
</select>
<select name="scheme" onchange="this.form.submit()">
<option value="">Select a scheme</option>
{% for scheme in schemes %}
<option value="{{ scheme.id }}"
{% if scheme.id == request.GET.scheme %}selected{% endif %}>
{{ scheme.name }}
</option>
{% endfor %}
</select>
</form>
{% for project in projects %}
<p>{{ project.name }}</p>
{% endfor %}
urls.py: 从 django.urls 导入路径 从.views导入project_view
urlpatterns = [
path('projects/', project_view, name='project_view'),
# other urls
]
我希望我正确理解了这个问题:0)