Python中如何使用django_filter根据choice字段的值进行过滤?

我有一个 model IPInfo

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)

然后我用 django_filter 做过滤

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 的值“内网“、“外网”做参数值,而不是“ 1 ”和“ 2 ”。

我该怎么做?


Python中如何使用django_filter根据choice字段的值进行过滤?

10 回复

IPInfoModel.TYPE_INTRANET


# 假设你的models.py中有这样的模型定义
from django.db import models

class MyModel(models.Model):
    STATUS_CHOICES = [
        ('active', '活跃'),
        ('inactive', '不活跃'),
        ('pending', '待处理'),
    ]
    status = models.CharField(max_length=10, choices=STATUS_CHOICES)
    name = models.CharField(max_length=100)

# 在filters.py中创建过滤器
import django_filters
from .models import MyModel

class MyModelFilter(django_filters.FilterSet):
    # 方法1:使用ChoiceFilter(推荐)
    status = django_filters.ChoiceFilter(
        choices=MyModel.STATUS_CHOICES,
        label='状态'
    )
    
    # 方法2:如果需要自定义查询行为
    status_exact = django_filters.ChoiceFilter(
        field_name='status',
        choices=MyModel.STATUS_CHOICES,
        method='filter_status',
        label='精确匹配状态'
    )
    
    def filter_status(self, queryset, name, value):
        # 这里可以添加自定义过滤逻辑
        return queryset.filter(status=value)
    
    class Meta:
        model = MyModel
        fields = ['status', 'name']

# 在views.py中使用
from django_filters.views import FilterView
from .models import MyModel
from .filters import MyModelFilter

class MyModelListView(FilterView):
    model = MyModel
    filterset_class = MyModelFilter
    template_name = 'mymodel_list.html'
    paginate_by = 20

# 或者在函数视图中使用
from django.shortcuts import render
from .filters import MyModelFilter

def mymodel_list(request):
    queryset = MyModel.objects.all()
    myfilter = MyModelFilter(request.GET, queryset=queryset)
    return render(request, 'mymodel_list.html', {'filter': myfilter})

# 在模板中(mymodel_list.html)使用
"""
<form method="get">
    {{ filter.form.as_p }}
    <button type="submit">筛选</button>
</form>

<table>
    {% for obj in filter.qs %}
        <tr>
            <td>{{ obj.name }}</td>
            <td>{{ obj.get_status_display }}</td>
        </tr>
    {% endfor %}
</table>
"""

核心就是使用django_filters.ChoiceFilter并传入模型的choices选项。这样会自动生成下拉选择框,用户可以选择预定义的值进行过滤。记得在FilterSetMeta类中指定模型和字段。

简单说就是:用ChoiceFilter绑定模型的choices字段。

A 佬?

? 这… 什么意思?

你可以写一个函数做映射,或者两个常量直接定义为字符串…

不过你数据库存的是 1,2 呀,
不过无论想怎么搞,一般在前端层面的去做 widgets 就 ok 了。

思路这么进去

看这个库的 225 行
<br>class ChoiceFilter(Filter):<br> field_class = ChoiceField # 这一行<br><br> def __init__(self, *args, **kwargs):<br> self.null_value = kwargs.get('null_value', settings.NULL_CHOICE_VALUE)<br> super(ChoiceFilter, self).__init__(*args, **kwargs)<br><br> def filter(self, qs, value):<br> if value != self.null_value:<br> return super(ChoiceFilter, self).filter(qs, value)<br><br> qs = self.get_method(qs)(**{'%s__%s' % (self.field_name, self.lookup_expr): None})<br> return qs.distinct() if self.distinct else qs<br>


然后追溯到 django 的 form widgets, 第 550 行,
s

<br> def render_option(self, selected_choices, option_value, option_label):<br> if option_value is None:<br> option_value = ''<br> option_value = force_text(option_value)<br> if option_value in selected_choices:<br> selected_html = mark_safe(' selected="selected"')<br> if not self.allow_multiple_selected:<br> # Only allow for a single selection.<br> selected_choices.remove(option_value)<br> else:<br> selected_html = ''<br> return format_html('&lt;option value="{}"{}&gt;{}&lt;/option&gt;', option_value, selected_html, force_text(option_label))<br><br>


然后再把 allow none 那个----也做个处理就 OK 了。

这个是显示用的,不能用于查询
多谢提供思路



你把 option_value 改为 lforce_text(option_label) 就可以了。

另外一种做法是,自定义 filter,做个 dict,key,value 再做一遍反向查询。

option_value 和 lforce_text 是 django_filter 里的参数吗?我在文档里没搜到?

回到顶部