django-filters——篩選 choices欄位
阿新 • • 發佈:2020-11-12
如果我們有這樣一個model:
class IPInfoModel(models.Model):
TYPE_INTRANET = 1
TYPE_INTERNET = 2
IP_TYPES = (
(TYPE_INTRANET, u'內網'),
(TYPE_INTERNET, u'外網'),
)
ip = models.GenericIPAddressField("IP", unique=True)
ip_type = models.SmallIntegerField(choices=IP_TYPES)
然後我使用 rest_frame_work + django_filter 做API
from django_filters import rest_framework as django_filters class IPInfoFilter(django_filters.FilterSet): ip_type = django_filters.ChoiceFilter(choices=IPInfoModel.IP_TYPES) class Meta: model = IPInfoModel fields = ["ip_type",] class IPInfoViewSet(mixins.ListModelMixin, viewsets.GenericViewSet): queryset = IPInfoModel.objects.all() serializer_class = IPInfoSerializer filter_class = IPInfoFilter
但是這樣在過濾 ip_type
時,只能使用choice field 的 key 值 “1”, “2” 來進行過濾。
這樣很不直觀,那麼如何才能使用choice field 的 value 值 “內網”, “外網” 來過濾物件呢?
方法一
我的做法是通過自定義一個 ChoiceValueFilter
,並覆蓋 Django 的 forms.ChoiceField
中的驗證函式 valid_value
來實現。
程式碼如下:
from django import forms from django_filters import rest_framework as django_filters from django_filters.conf import settings class ChoiceValueField(forms.ChoiceField): def valid_value(self, value): text_value = str(value) for k, v in self.choices: if value == v or text_value == str(v): return True return False class ChoiceValueFilter(django_filters.Filter): field_class = ChoiceValueField def __init__(self, *args, **kwargs): self.null_value = kwargs.get('null_value', settings.NULL_CHOICE_VALUE) self.choices = kwargs.get('choices') super().__init__(*args, **kwargs) def filter(self, qs, value): value_map = {v: k for k, v in self.choices} return qs.filter(ip_type=value_map[value]) if value else qs
這樣做的好處是
- 比較通用,只要傳入對應choice field 的元祖就可以使用了。
- 仍然會對傳入的引數進行驗證,保證只能使用 choice field 的 value 值中已有的值進行過濾。
然後改寫原有的IPInfoFilter
, 改為使用 ChoiceValueFilter
就可以了。
class IPInfoFilter(django_filters.FilterSet):
ip_type = ChoiceValueFilter(choices=IPInfoModel.IP_TYPES)
class Meta:
model = IPInfoModel
fields = ["ip_type", "is_vip"]
方法二
也可以通過自定義filter的方法來實現
參考: https://django-filter.readthedocs.io/en/master/ref/filters.html#method
class IPInfoFilter(django_filters.FilterSet):
ip_type = django_filters.CharFilter(method='filter_ip_type')
def filter_ip_type(self, queryset, name, value):
# create a dictionary string -> integer
value_map = {v: k for k, v in IPInfoModel.IP_TYPES.items()}
# get the integer value for the input string
value = value_map[value]
return queryset.filter(ip_type=value)
原文連結:https://www.cnblogs.com/leisurelylicht/p/ru-he-zaidjangofilter-zhong-yongchoice-field-de-va.html