ModelForm clean_xxxx()适用于CharField,不适用于URLField。 Django 1.5

时间:2013-11-08 20:31:50

标签: django django-models django-forms

如何在验证URLField之前删除空格?

使用“clean_ [fieldname]()”似乎是来自https://docs.djangoproject.com/en/dev/ref/forms/validation/的文档化方式,但它不适用于URLField。我把它简化为一个基本的测试用例,可以在django shell中运行:

class XXXTestModel(models.Model):
    url  = models.URLField('URL',null=True,blank=True)
    name = models.CharField(max_length=200)
class XXXTestForm(ModelForm):
    def clean_url(self):
        return self.cleaned_data['url'].strip()
    def clean_name(self):
        return self.cleaned_data['name'].strip() 
    class Meta:
        model = XXXTestModel
        fields = (
             'url',
        )

使用:

从Django shell测试
>>> django.VERSION
(1, 5, 1, 'final', 0)
>>> from xxx import XXXTestForm,XXXTestModel
>>> data = dict(url=' http://www.example.com/ ',name=' example ')
>>> f=XXXTestForm(data)
>>> f.is_valid();f.errors
False
{'url': [u'Enter a valid URL.']}
>>> f.cleaned_data
{'name': example'}

这个问题有很多关于堆栈溢出的密切欺骗,但没有一个答案指导解决方案。

1 个答案:

答案 0 :(得分:7)

这里的问题是django.forms.URLField如何运作。

django.forms.Field.clean定义为:

def clean(self, value):
    """
    Validates the given value and returns its "cleaned" value as an
    appropriate Python object.

    Raises ValidationError for any errors.
    """
    value = self.to_python(value)
    self.validate(value)
    self.run_validators(value)
    return value

请注意,to_python在任何验证之前执行。这是问题所在 - django.forms.URLField无法理解你给它的值,因此它产生的值使得已经定义为django.forms.URLField一部分的验证器集失败(即django.core.validators.URLValidator })。

失败的原因是django尝试“规范化”URL。这包括在需要的地方添加"http://"等内容。在给出您的示例网址" http://www.example.com "时,django使用urlparse.urlsplit来获取网址的“部分”。然而,领先的空间将其混乱,整个价值成为path的一部分。因此,django找不到scheme,并将网址重新设为"http:// http://www.example.com "。然后将其提供给django.core.validators.URLValidator,这显然会失败。

为避免这种情况,我们需要为表单

定义自己的URLField
from django import forms

class StrippedURLField(forms.URLField):
    def to_python(self, value):
        return super(StrippedURLField, self).to_python(value and value.strip())

使用它可以确保流程完全符合预期,我们不需要clean_url方法。 (注意:你应尽可能使用clean_*,但这里不是)

class XXXTestForm(forms.ModelForm):
    url = StrippedURLField(blank=True, null=True)