Django 3.2跨深度关联模型筛选外键选项失效问题排查
问题原因及解决方案
核心问题1:父类Admin方法覆盖了你的queryset设置
你当前的写法是先给kwargs["queryset"]赋值,再调用父类的formfield_for_foreignkey方法,如果父类OperationProxyAdmin也对structure字段的查询集做了修改,会直接覆盖你设置的参数,导致筛选不生效。
核心问题2:跨user关联查询返回空的原因
你的Customer模型的user字段设置了null=True,默认使用customer__user__xxx进行筛选时,Django会采用内连接查询,自动排除所有user字段为null的Customer对应的Structure,如果你测试的Structure关联的Customer没有绑定用户,或者绑定的用户不符合筛选条件,就会返回空结果。
排查步骤
- 先验证查询语法本身是否正确,打开Django shell执行以下代码,确认返回结果符合预期:
from your_app.models import Structure # 查看符合活跃用户条件的结构数量 print(Structure.objects.filter(customer__user__is_active=True).count()) # 查看测试用户对应的结构数量 print(Structure.objects.filter(customer__user__username="user480").count())
如果shell查询结果和预期一致,说明查询逻辑没有问题,问题出在Admin方法的执行顺序。
修复代码
修改formfield_for_foreignkey的写法,先调用父类方法获取默认表单字段,再修改其查询集,避免被父类覆盖:
def formfield_for_foreignkey(self, db_field, request, **kwargs): """Override formfield_for_foreignkey.""" formfield = super().formfield_for_foreignkey(db_field, request, **kwargs) if db_field.name == "structure": # 如果需要保留父类原有筛选条件,就用 formfield.queryset.filter(xxx) # 如果需要完全替换查询集,就直接赋值Structure.objects.filter(xxx) formfield.queryset = formfield.queryset.filter(customer__user__is_active=True) # 如果需要保留未绑定用户的Customer对应的Structure,用下面的Q查询写法 # from django.db.models import Q # formfield.queryset = formfield.queryset.filter( # Q(customer__user__isnull=True) | Q(customer__user__is_active=True) # ) return formfield
内容的提问来源于stack exchange,提问作者Darko Romanov
相关产品推荐
相关产品推荐

