首页 > 解决方案 > 在 django-filters 中为过滤 ChoiceField 创建别名

问题描述

对于某些模型,我有以下过滤器集:

class MyModel(models.Model):
    STATUS_ACTIVE = 0
    STATUS_DONE = 1
    STATUSES = (
        (STATUS_ACTIVE, 'Active'),
        (STATUS_DONE, 'Done'),
    )

    status = models.IntegerField(choices=STATUSES, default=STATUS_ACTIVE)


class ModelFilter(FilterSet):
    status = ChoiceFilter(choices=MyModel.STATUSES)

    class Meta:
        model = MyModel
        fields = (
            'status',
        )

当我向某些 API 发出请求时,我应该使用 status 作为数字 - /app/model?status=0

如何为其制作别名,以便我可以使用/app/model?status=active而不是数字,而无需更改模型?

标签: pythondjangodjango-rest-frameworkdjango-filter

解决方案


最后我在库中找到了源代码并创建了这个解决方案:

class AliasesChoiceField(ChoiceField):
    def valid_value(self, value):
        for _, v in self.choices:
            if value == v or str(value) == str(v):
                return True
        return False

class AliasesChoiceFilter(ChoiceFilter):
    field_class = AliasesChoiceField
    def filter(self, qs, value):
        for val, alias in self.extra['choices']:
            if value == alias:
                return super(AliasesChoiceFilter, self).filter(qs, val)
        return super(AliasesChoiceFilter, self).filter(qs, value)

AliasesChoiceField.valid_value()我刚刚复制了基本方法并进行了简单的更改以正确验证值。

AliasesChoiceFilter用于field_class验证,所以我只需将其替换为ChoiceFilter, 并将filter()方法更改为我选择的参数的正确映射。

最后我的模型看起来像这样

class ModelFilter(FilterSet):
    STATUSES = (
        (MyModel.STATUS_ACTIVE, 'active'),
        (MyModel.STATUS_DONE, 'done')
    )
    status = AliasesChoiceFilter(choices=STATUSES)

并且 API 路由/app/model?status=active正常工作,设置正确的过滤状态。

但!此解决方案不适用于 Django Admin 或通过 web api 界面,因为它将提供真实值,并且会尝试传递0而不是active


推荐阅读