如何将Request传递给django-filters自定义Widget以保留筛选状态
如何将Request传递到Django Filter的自定义Widget中以保留筛选状态
我想创建一个类似Fiverr的筛选器(下拉选择搭配输入选项的组合),目前已完成Widget的创建,可选择选项并提交子输入值,但页面加载后值会丢失且无选项被选中。原因是单选框的值需要从request.GET中获取,但我无法将Request传递到Widget里——Filter类中可以拿到Request,但Widget中拿不到。
现有代码
自定义Widget代码
class PriceFilter(Widget): template_name = 'widgets/PriceFilter.html' def __init__(self, attrs=None, min=0, max=5000, *args, **kwargs): self.min = min self.max = max # print(self.request) print(kwargs) super(PriceFilter, self).__init__() def get_context(self, name, value, attrs=None, *args, **kwargs): context = super().get_context(name, value, attrs) context['min'] = self.min context['max'] = self.max print(self.request) print(kwargs) return context
Widget模板代码
<div class="btn-group"> <button class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenuClickableInside" data-bs-toggle="dropdown" data-bs-auto-close="outside" aria-expanded="false"> Clickable inside </button> <ul class="dropdown-menu" aria-labelledby="dropdownMenuClickableInside"> <div class="form-check"> <input class="form-check-input" type="radio" name="{{widget.name}}__selector" id="{{widget.name}}__selector" #if request.GET.price__selector==1 then ceck this#> <label class="form-check-label" for="{{widget.name}}__selector"> Default radio </label> <div class="input-filters"> <input type="hidden" name="{{widget.name}}__lt" id="id_{{widget.name}}__lt" value="{{widget.value|default_if_none:''}}"> </div> </div> </ul> </div>
Filter类代码
class ListFilter(django_filters.FilterSet): price = django_filters.NumberFilter(widget=PriceFilter()) class Meta: model = MainModel fields = { 'name':['contains'],'price':[] } def __init__(self, *args, **kwargs): print('kwgs') # print(args[0]) self.request = kwargs.pop('request', None) super(ListFilter, self).__init__(*args, **kwargs) self.filters["price"].request = args[0]
解决方案
步骤1:在Filter初始化时给Widget传递Request
修改Filter的__init__方法,直接为目标Widget实例添加request属性:
class ListFilter(django_filters.FilterSet): price = django_filters.NumberFilter(widget=PriceFilter()) class Meta: model = MainModel fields = { 'name':['contains'],'price':[] } def __init__(self, *args, **kwargs): self.request = kwargs.pop('request', None) super(ListFilter, self).__init__(*args, **kwargs) # 给price字段的Widget传递request if self.request: self.filters["price"].widget.request = self.request
步骤2:修改Widget将Request传入模板上下文
调整Widget的__init__和get_context方法,确保request能被传递到模板:
class PriceFilter(Widget): template_name = 'widgets/PriceFilter.html' def __init__(self, attrs=None, min=0, max=5000, *args, **kwargs): self.min = min self.max = max self.request = None # 初始化request属性 super().__init__(attrs) # 正确传递attrs给父类 def get_context(self, name, value, attrs=None): context = super().get_context(name, value, attrs) context['min'] = self.min context['max'] = self.max # 将request加入模板上下文 if hasattr(self, 'request'): context['request'] = self.request return context
步骤3:修改模板实现选中状态判断
用Django模板语法替换伪代码,实现单选框的选中逻辑:
<div class="btn-group"> <button class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenuClickableInside" data-bs-toggle="dropdown" data-bs-auto-close="outside" aria-expanded="false"> Clickable inside </button> <ul class="dropdown-menu" aria-labelledby="dropdownMenuClickableInside"> <div class="form-check"> <input class="form-check-input" type="radio" name="{{widget.name}}__selector" id="{{widget.name}}__selector" {% if request.GET.price__selector == '1' %}checked{% endif %}> <label class="form-check-label" for="{{widget.name}}__selector"> Default radio </label> <div class="input-filters"> <input type="hidden" name="{{widget.name}}__lt" id="id_{{widget.name}}__lt" value="{{request.GET.price__lt|default_if_none:''}}"> </div> </div> </ul> </div>
额外注意
确保在视图中初始化FilterSet时传入request参数:
filter = ListFilter(request.GET, queryset=MainModel.objects.all(), request=request)
内容的提问来源于stack exchange,提问作者El hosayn Ait Ali
相关产品推荐
相关产品推荐

