【问题标题】:Where to clean extra whitespace from form field inputs?在哪里清除表单字段输入中的额外空白?
【发布时间】:2012-01-09 09:09:37
【问题描述】:

我刚刚发现 Django 不会自动从表单字段输入中去除多余的空格,我想我理解其中的原理(“框架不应该改变用户输入”)。

我想我知道如何使用 python 的 re: 删除多余的空格

#data = re.sub('\A\s+|\s+\Z', '', data)
data = data.strip()
data = re.sub('\s+', ' ', data)

问题是我应该在哪里执行此操作?大概这应该发生在表单的干净阶段之一,但是哪一个?理想情况下,我想清理我所有的额外空白字段。如果它应该在 clean_field() 方法中完成,那就意味着我必须有很多 clean_field() 方法基本上做同样的事情,这似乎是很多重复。

如果不是表单的清理阶段,那么可能在表单所基于的模型中?

【问题讨论】:

  • 您可能还想考虑更简单的strip 函数,而不是使用正则表达式
  • 你可以使用data = data.strip() 代替第一行(丑陋的)。
  • @Michael, julio - 啊,谢谢!我认为 strip 只删除字符串末尾的空格......相应地编辑了问题。
  • 你的第二行还有一个常用的方法:data = ' '.join(data.split())
  • Python strip() 删除前导和尾随空格@Westerley

标签: django


【解决方案1】:

从 Django 1.9 开始,您可以在表单定义的字段中使用 strip 关键字参数:

条带¶ Django 1.9 中的新功能。

If True (default), the value will be stripped of leading and trailing whitespace.

应该给出类似的内容:

class MyForm(forms.Form):

    myfield = forms.CharField(min_length=42, strip=True)

因为它的默认值是True 这应该是自动的django>=1.9

这也与RegexField有关。

【讨论】:

    【解决方案2】:

    如果你想在你的项目中剥离()每个 CharField; monkeypatch CharField 的默认清理方法可能是最简单的。

    在:monkey_patch/__init__.py

    from django.forms.fields import CharField
    
    def new_clean(self, value):
        """ Strip leading and trailing whitespace on all CharField's """
        if value:
            # We try/catch here, because other fields subclass CharField. So I'm not totally certain that value will always be stripable.
            try:
                value = value.strip()
            except:
                pass
        return super(CharField, self).clean(value)
    
    CharField.clean = new_clean
    

    【讨论】:

      【解决方案3】:

      使用以下混合:

      class StripWhitespaceMixin(object):
      
          def full_clean(self):
              # self.data can be dict (usually empty) or QueryDict here.
              self.data = self.data.copy()
              is_querydict = hasattr(self.data, 'setlist')
              strip = lambda val: val.strip()
              for k in list(self.data.keys()):
                  if is_querydict:
                      self.data.setlist(k, map(strip, self.data.getlist(k)))
                  else:
                      self.data[k] = strip(self.data[k])
              super(StripWhitespaceMixin, self).full_clean()
      

      将此作为 mixin 添加到您的表单中,例如:

      class MyForm(StripWhitespaceMixin, Form):
          pass
      

      这类似于 pymarco 的回答,但不涉及复制粘贴然后修改 Django 代码(_clean_fields 方法的内容)。

      相反,它会覆盖full_clean,但在对输入数据进行一些调整后调用原始的full_clean 方法。这使得它更少依赖 Django 的 Form 类的实现细节,这些细节可能会改变(事实上,自那个答案以来已经改变了)。

      【讨论】:

        【解决方案4】:

        将其添加到表单中的def clean(self): 怎么样?

        有关更多文档,请参阅: https://docs.djangoproject.com/en/dev/ref/forms/validation/#cleaning-and-validating-fields-that-depend-on-each-other

        您的方法可能如下所示:

        def clean(self):
          cleaned_data = self.cleaned_data
          for k in self.cleaned_data:
            data = re.sub('\A\s+', '', self.cleaned_data[k])
            data = re.sub('\s+\Z', '', data)
            data = re.sub('\s+', ' ', data)
            cleaned_data[k]=data
          return cleaned_data
        

        【讨论】:

        • 你可以使用data = data.strip() 代替前两行(丑陋的)。
        • @Arthur - 您是否还必须在自定义清理中调用 to_python()、validate() 和 run_validators()?
        • cleaned_data = super(YourFormClass, self).clean(),因为第 1 行维护父行为
        【解决方案5】:

        我的方法是借鉴here。但我没有继承 django.forms.Form,而是使用了 mixin。这样我就可以将它与FormModelForm 一起使用。这里定义的方法覆盖了BaseForm_clean_fields方法。

        class StripWhitespaceMixin(object):
            def _clean_fields(self):
                for name, field in self.fields.items():
                    # value_from_datadict() gets the data from the data dictionaries.
                    # Each widget type knows how to retrieve its own data, because some
                    # widgets split data over several HTML fields.
                    value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name))
        
                    try:
                        if isinstance(field, FileField):
                            initial = self.initial.get(name, field.initial)
                            value = field.clean(value, initial)
                        else:
                            if isinstance(value, basestring):
                                value = field.clean(value.strip())
                            else:
                                value = field.clean(value)
                        self.cleaned_data[name] = value
                        if hasattr(self, 'clean_%s' % name):
                            value = getattr(self, 'clean_%s' % name)()
                            self.cleaned_data[name] = value
                    except ValidationError as e:
                        self._errors[name] = self.error_class(e.messages)
                        if name in self.cleaned_data:
                            del self.cleaned_data[name]
        

        要使用,只需将 mixin 添加到您的表单中

        class MyForm(StripeWhitespaceMixin, ModelForm):
            ...
        

        此外,如果您想在保存没有表单的模型时修剪空白,您可以使用以下 mixin。默认情况下不验证没有表单的模型。当我根据从外部 rest api 调用返回的 json 数据创建对象时,我会使用它。

        class ValidateModelMixin(object):
            def clean(self):
                for field in self._meta.fields:
                    value = getattr(self, field.name)
        
                    if value:
                        # ducktyping attempt to strip whitespace
                        try:
                            setattr(self, field.name, value.strip())
                        except Exception:
                            pass
        
            def save(self, *args, **kwargs):
                self.full_clean()
                super(ValidateModelMixin, self).save(*args, **kwargs)
        

        然后在你的models.py中

        class MyModel(ValidateModelMixin, Model):
            ....
        

        【讨论】:

        • 最佳答案恕我直言。谢谢!
        • 唯一缺少的是super(ValidateModelMixin, self).clean(),以防它应用到的模型已经有一个干净的方法。
        • StripWhitespaceMixin 现在是有问题的,因为最近的 Django 版本已经改变了 _clean_fields 方法的实现,所以它错过了基类中的一些行为。请参阅下面的我的答案,它不会有这个问题。
        【解决方案6】:

        创建一个自定义模型字段,以便自动使用您的自定义表单字段。

        class TrimmedCharFormField(forms.CharField):
            def clean(self, value):
                if value:
                    value = value.strip()
                return super(TrimmedCharFormField, self).clean(value)
        
        # (If you use South) add_introspection_rules([], ["^common\.fields\.TrimmedCharField"])
        class TrimmedCharField(models.CharField):
            __metaclass__ = models.SubfieldBase
        
            def formfield(self, **kwargs):
                return super(TrimmedCharField, self).formfield(form_class=TrimmedCharFormField, **kwargs)
        

        然后在您的模型中将django.db.models.CharField 替换为TrimmedCharField

        【讨论】:

          【解决方案7】:

          执行此操作的一种方法是指定去除空格的自定义表单小部件:

          >>> from django import forms
          >>> class StripTextField(forms.CharField):
          ...     def clean(self,value):
          ...         return value.strip()
          ...         
          >>> f = StripTextField()
          >>> f.clean('  hello  ')
          'hello'
          

          然后在你的 ModelForm 中使用它:

          class MyForm(ModelForm):
              strip_field = StripTextField()
          
              class Meta:
                  model = MyModel
          

          但是,您认为这样做的最佳位置是表单经过验证之后;如果您使用ModelForms,则在您对数据库进行任何插入或其他数据操作之前。

          您始终可以创建自己的非ModelForm 表单并以这种方式控制字段和验证的各个方面。

          ModelForm 的验证添加了对违反 db 约束的值的检查;因此,如果该字段可以接受' hello ' 作为有效输入,ModelForm 的is_valid() 将没有理由去除空格(因为它不会产生任意干净的逻辑,除了你提到的“框架不应该改变用户的输入”)。

          【讨论】:

            【解决方案8】:

            在这种情况下,创建自己的表单域可能会很有用(听起来并不难)。在 clean() 方法中,您将删除多余的空格。

            引用文档:

            您可以轻松创建自定义字段类。为此,只需创建一个 django.forms.Field 的子类。它唯一的要求是它 实现一个 clean() 方法并且它的 __init__() 方法接受 核心参数(必需,标签,初始,小部件, 帮助文本)。

            更多信息:https://docs.djangoproject.com/en/1.3/ref/forms/fields/#creating-custom-fields

            【讨论】:

            • 这看起来很有希望!它可以更接近源头解决问题。但是,我使用的是 modelForms,因此要更改与每个模型字段关联的表单字段,我还必须在表单规范中“重新声明”所有修改后的字段。有没有解决的办法?我是否还必须自定义模型字段才能使用修改后的表单字段?
            猜你喜欢
            • 1970-01-01
            • 2022-11-17
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-07-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多