如何在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
相关产品推荐
相关产品推荐

