我使用django-filter
为大型数据集提供搜索表单。它适用于那些想要提供样本ID并获得过滤列表的科学家。
为了这个目的,我想要<textarea>
而不是<input type="text">
,因为他们只会一次粘贴十几个或更多的ID,如果他们对提交的值有概述会很好。
这是我的代码:
from django import forms
import django_filters as filters
class TextareaCSVWidget(filters.widgets.BaseCSVWidget, forms.Textarea):
"""
The widget should create textarea.
"""
pass
class CharInFilter(filters.BaseInFilter, filters.CharFilter):
"""
The filter should accept coma separated strings.
"""
pass
class SampleFilter(filters.FilterSet):
sample_ids = CharInFilter(
name='sample_id',
widget=TextareaCSVWidget()
)
现在textarea正确显示。搜索按预期工作。
然而,有一个奇怪的问题:当我输入多个值(以逗号分隔)时,在提交搜索表单后,textarea被<input type="text">
替换(输入的值被保留)。这看起来很尴尬,我需要防止这种行为。
如果我在textarea中输入单个值,则不会发生这种情况。
我的做法有什么问题?
编辑:
在发布问题之后,我在django-filter
的源代码中进行了更好的查看,这里是我用来创建BaseCSVWidget
的类TextareaCSVWidget
的相关部分:
def render(self, name, value, attrs=None):
if not self._isiterable(value):
value = [value]
if len(value) <= 1:
# delegate to main widget (Select, etc...) if not multiple values
value = value[0] if value else ''
return super(BaseCSVWidget, self).render(name, value, attrs)
# if we have multiple values, we need to force render as a text input
# (otherwise, the additional values are lost)
surrogate = forms.TextInput()
value = [force_text(format_value(surrogate, v)) for v in value]
value = ','.join(list(value))
return surrogate.render(name, value, attrs)
强调的部分是决定性的。我现在应该怎么做?覆盖render
方法看起来并不像我。我不想丢失输入的值。
我可以使用JavaScript操作它,并在文档加载时用textarea替换输入元素,如果没有别的帮助。
我已经覆盖了render
方法,它工作正常。这是我的解决方案:
def render(self, name, value, attrs=None):
if not self._isiterable(value):
value = [value]
if len(value) <= 1:
# delegate to main widget (Select, etc...) if not multiple values
value = value[0] if value else ''
return super(TextareaCSVWidget, self).render(name, value, attrs)
value = ','.join(value)
return super(TextareaCSVWidget, self).render(name, value, attrs)
强调的部分是变化。这会正确呈现textarea
并保留用户输入。我想作者在创建这个小部件时没有考虑HTML元素textarea
,因此强制使用input
类型的text
元素。例如在<input type="number">
中,不可能放置多个彗差分隔值。
如果有人有更好的解决方案,我仍然很乐意得到更好的答案。