You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Django Admin中通过带Through的多对多字段实现学校过滤

解决Django Admin中按项目关联人员所属学校过滤Project的问题

我有以下Django模型:

class Subject(models.Model):
    name = models.CharField(max_length=200)

class Person(models.Model):
    subject = models.ForeignKey(Subject, on_delete=models.CASCADE, blank=True, null=True)
    school = models.ForeignKey(School, on_delete=models.CASCADE, blank=True, null=True)

class PersonRole(models.Model):
    project = models.ForeignKey('Project', on_delete=models.CASCADE)
    person = models.ForeignKey(Person, on_delete=models.CASCADE)

class Project(models.Model):
    title = models.CharField(max_length=200)
    person = models.ManyToManyField(Person, through=PersonRole)

想要在Django Admin的Project列表页添加筛选器,实现按项目关联人员所属的学校过滤——比如如果"第一学校"的John关联了项目3,筛选后列表只显示项目3。

我尝试给模型加了方法并配置Admin,但运行报错:'ManyRelatedManager' object has no attribute 'get_school',尝试的代码如下:

models.py修改:

class PersonRole(models.Model):
    [...]
    def get_school(self):
        return self.person.school

class Project(models.Model):
    @admin.display(description='PI School')
    def get_PI_school(self):
        return [p for p in self.person.get_school()] 

admin.py:

class ProjectAdmin(admin.ModelAdmin):
    list_display = ("get_PI_school",) # 先测试字段是否能显示

报错原因

self.person是Django的ManyRelatedManager对象(对应多对多关联),它本身没有get_school方法。你需要遍历关联的Person实例,再逐个获取每个Person的school属性。


完整解决方案

1. 修复Project的显示字段方法

先把列表页显示学校的方法改对,避免重复显示同一学校:

class Project(models.Model):
    title = models.CharField(max_length=200)
    person = models.ManyToManyField(Person, through=PersonRole)

    @admin.display(description='关联学校')
    def get_PI_school(self):
        # 用集合去重,再拼接成字符串
        related_schools = set()
        for person in self.person.all():
            if person.school:
                related_schools.add(person.school.name)
        return ', '.join(related_schools)

2. 自定义学校筛选器

在admin.py中创建自定义SimpleListFilter,实现筛选逻辑:

from django.contrib import admin
from django.contrib.admin import SimpleListFilter
from .models import Project, School

class ProjectSchoolFilter(SimpleListFilter):
    title = '所属学校'  # 筛选器在页面上的显示名称
    parameter_name = 'school'  # URL中使用的参数标识

    def lookups(self, request, model_admin):
        # 只获取关联过项目的学校,避免显示无关联的无效选项
        related_school_ids = Project.objects.filter(person__school__isnull=False)\
            .values_list('person__school__id', flat=True).distinct()
        return [(school.id, school.name) for school in School.objects.filter(id__in=related_school_ids)]

    def queryset(self, request, queryset):
        # 根据选中的学校过滤项目,distinct()避免重复结果
        if self.value():
            return queryset.filter(person__school_id=self.value()).distinct()
        return queryset

3. 配置ProjectAdmin

把筛选器和显示字段添加到Admin配置:

class ProjectAdmin(admin.ModelAdmin):
    list_display = ('title', 'get_PI_school')  # 显示项目标题和关联学校
    list_filter = (ProjectSchoolFilter,)  # 启用自定义学校筛选器

admin.site.register(Project, ProjectAdmin)

配置完成后,Admin的Project列表页会出现"所属学校"筛选器,选择对应学校就能过滤出所有关联该学校人员的项目,同时列表页也会清晰显示每个项目关联的学校名称。

内容的提问来源于stack exchange,提问作者HBMCS

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 21:22:43