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

如何在Django-filter模型中按请求用户配置ModelChoiceFilter查询集

问题:如何在Django-filter的ModelChoiceFilter中传入当前用户对应的查询集

我正在使用django-filter包,想实现在ModelChoiceFilter中传入当前请求用户对应的查询集,但修改视图后__init__函数没达到预期效果,相关代码如下:

filters.py

class PresenceDateFilter(django_filters.FilterSet):

    work_date = DateTimeFromToRangeFilter(
        widget=django_filters.widgets.RangeWidget(
            attrs={'type': 'date', 'class': 'form-control'},
        ))

    class Meta:
        model = PresenceDetailInfo
        fields = ('presence_info__counter_presence',
                  'work_date',
                  )

models.py

class PresenceDetailInfo(models.Model):
    presence_info = models.ForeignKey(PresenceListInfo, default=None, null=True,
                                      on_delete=models.CASCADE, related_name='details_info')

class PresenceListInfo(models.Model):
    counter_presence = models.ForeignKey(CounterParty, default=None, null=True,
                                         on_delete=models.CASCADE)

class CounterParty(models.Model):
    counter_user = models.ManyToManyField(User, blank=True, related_name='counter_user')

views.py

class ShowPresenceInfoList(AccessMixin, ListView):
    model = PresenceDetailInfo
    template_name = 'common/presence_info.html'
    context_object_name = 'details_list'

    def get_context_data(self, *, object_list=None, **kwargs):
        context = super(ShowPresenceInfoList, self).get_context_data(**kwargs)
        list_of_dates = PresenceDateFilter(self.request.GET, queryset=self.get_queryset())
        context['form'] = list_of_dates.form
        context['dates'] = list_of_dates.qs

        return context

    def get_queryset(self):
        current_user = self.request.user
        current_counters = CounterParty.objects.filter(counter_user=current_user)

        if current_user.is_superuser:
            qs = PresenceDetailInfo.objects.all().select_related('presence_info', ).order_by(
                'work_date')
        else:
            qs = PresenceDetailInfo.objects.filter(presence_info__counter_presence__in=current_counters).select_related(
                'presence_info', ).order_by('work_date')
        word = PresenceDateFilter(self.request.GET, queryset=qs, request=self.request)
        return word.qs

解决方案

1. 修改FilterSet类,重写__init__方法

显式定义目标ModelChoiceFilter,并在初始化时根据当前用户调整查询集:

import django_filters
from .models import PresenceDetailInfo, CounterParty

class PresenceDateFilter(django_filters.FilterSet):
    work_date = django_filters.DateTimeFromToRangeFilter(
        widget=django_filters.widgets.RangeWidget(
            attrs={'type': 'date', 'class': 'form-control'},
        ))
    # 显式声明ModelChoiceFilter,便于后续修改查询集
    presence_info__counter_presence = django_filters.ModelChoiceFilter(
        queryset=CounterParty.objects.all(),
        label='合作方'
    )

    class Meta:
        model = PresenceDetailInfo
        fields = ('presence_info__counter_presence', 'work_date',)

    def __init__(self, *args, **kwargs):
        # 提取request参数
        self.request = kwargs.pop('request', None)
        super().__init__(*args, **kwargs)
        
        # 非超级用户仅显示自身关联的合作方
        if self.request and not self.request.user.is_superuser:
            self.filters['presence_info__counter_presence'].queryset = CounterParty.objects.filter(
                counter_user=self.request.user
            )

2. 调整视图逻辑,确保传入request参数

拆分基础查询集和过滤逻辑,避免重复实例化FilterSet:

class ShowPresenceInfoList(AccessMixin, ListView):
    model = PresenceDetailInfo
    template_name = 'common/presence_info.html'
    context_object_name = 'details_list'

    def get_context_data(self, *, object_list=None, **kwargs):
        context = super().get_context_data(**kwargs)
        # 实例化FilterSet时传入request
        filter_set = PresenceDateFilter(self.request.GET, queryset=self.get_base_queryset(), request=self.request)
        context['form'] = filter_set.form
        context['dates'] = filter_set.qs
        return context

    def get_base_queryset(self):
        # 处理基础权限过滤,返回未经过滤的基础查询集
        current_user = self.request.user
        if current_user.is_superuser:
            return PresenceDetailInfo.objects.all().select_related('presence_info').order_by('work_date')
        else:
            current_counters = CounterParty.objects.filter(counter_user=current_user)
            return PresenceDetailInfo.objects.filter(
                presence_info__counter_presence__in=current_counters
            ).select_related('presence_info').order_by('work_date')

    def get_queryset(self):
        filter_set = PresenceDateFilter(self.request.GET, queryset=self.get_base_queryset(), request=self.request)
        return filter_set.qs

说明

  • 显式定义presence_info__counter_presence为ModelChoiceFilter,才能在__init__中动态修改其查询集
  • FilterSet的__init__方法接收request参数,根据用户身份筛选可选项,超级用户能看到全部合作方,普通用户仅显示自身关联的
  • 视图拆分get_base_queryset避免重复代码,确保实例化FilterSet时始终传入request参数

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 20:13:35