问题
I'm using django-filter in 2 places: My Django Rest Framework API, and in my FilterViews (Django Filter's Generic ListViews.) In the case of my FilterViews I'm showing both select boxes (ModelChoiceFilter) and multi-select boxes (ModelMultipleChoiceFilter) to be filtered on.
I need to be able to limit what's in those select and multi-select inputs based on a field inside the request.
It's relatively simple to change what's listed as a kwarg in the relevant field in the FilterSet. For example, here's my FilterSet where the queryset is set as a kwarg:
class FieldFilter(django_filters.FilterSet):
"""Filter for the field list in the API"""
dataset = ModelChoiceFilter(queryset=Dataset.objects.all())
class Meta(object):
"""Meta options for the filter"""
model = Field
fields = ['dataset']
And it's relatively straightforward to limit what the result is in DRF inside the get_queryset()
method. For example, here's my DRF ViewSet:
class FieldViewSet(viewsets.ReadOnlyModelViewSet):
"""A ViewSet for viewing dataset fields"""
queryset = Field.objects.all()
serializer_class = FieldSerializer
filter_class = FieldFilter
def get_queryset(self):
"""Get the queryset"""
queryset = super(FieldViewSet, self).get_queryset()
queryset = queryset.filter(
dataset__organization=self.request.organization)
return queryset
I just can't find anywhere to edit the Dataset
field in the filter_class
when the view is being displayed.
This is super straightforward in Django FormView
generic views, but it doesn't appear that FieldViewSet
follows the same get_form()
structure as generic views. It's also relatively straightforward to do in the admin, but DRF/Django-Filter don't seem to follow that structure either.
Is there any way to customize the queryset in those inputs on a per-request basis? Preferably both on FilterViews and in the HTML API browser, but just in FilterViews would be fine if it's too complicated for the HTML API browser.
回答1:
After hours of search, I found the solution in the official documentation here!
The queryset argument for ModelChoiceFilter
and ModelMultipleChoiceFilter
supports callable behavior. If a callable is passed, it will be invoked with the request
as its only argument.
import django_filters as filters
from django.utils.translation import gettext as _
def ourBranches(request):
if request is None:
return Branch.objects.none()
company = request.user.profile.company
return Branch.objects.filter(company=company)
class UnitFilter(filters.FilterSet):
branch = filters.ModelChoiceFilter(
queryset=ourBranches, empty_label=_("All Branches"))
class Meta:
model = Unit
fields = ('branch', )
and in the view, I made sure to pass the request
as well
qs = Unit.objects.all()
filter = UnitFilter(self.request.GET, request=self.request, queryset=qs)
table = UnitTable(filter.qs)
回答2:
I also had problems finding a resolution to this.
I solved it (I think) via the following:
views.py
table_filter = ExampleFilter(request.GET, kwarg_I_want_to_pass=request.user, queryset=qs)
filters.py
class ExampleFilter(django_filters.FilterSet):
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('kwarg_I_want_to_pass', None)
super(ExampleFilter, self).__init__(*args, **kwargs)
self.filters['field_to_filter'].extra.update({
'queryset': Supplier.objects.filter(related_user=self.user),
'empty_label': '',
'help_text': False
})
class Meta:
model = ExampleModel
fields = ['related_user', 'field_to_filter', ... other fields]
来源:https://stackoverflow.com/questions/50206820/customize-queryset-in-django-filter-modelchoicefilter-select-and-modelmultiple