当form.is_valid()为false时如何访问数据

55

当我有一个有效的Django表单时,我可以使用form.cleaned_data访问数据。但是,当表单无效时,即form.is_valid为false时,我如何获取用户输入的数据。

我正在尝试访问表单集中的表单,所以form.data似乎只会给我带来混乱。


2
相关的问题已经得到解决。Django 1.5版本将不会在表单无效时删除cleaned_data数据:https://code.djangoproject.com/ticket/5524 - guettli
7个回答

55
你可以使用。
form.data['field_name']

这种方式可以获取分配给字段的原始值。


然而,看起来这个字典并没有保存原始数据,假设有一个文件被提交。例如,假设您的表单上有一个名为“logo”的文件字段。如果您在未指定标志的情况下提交表单,则form.data['logo'] = ""。但是,如果您指定了一个标志,form.data['logo']就不存在了。有人知道它去哪里了吗? - Josh
傻瓜Josh,它应该放在form.files中——通过iPython内省和实际查看字段名称,你可以发现很多惊人的东西:)。 - Josh
如果该字段为空,您会如何处理?如果该字段为空,它会报错。 - Fuad
2
@Fuad form.data.get('field_name', None) @Fuad form.data.get('field_name', None) - Escher

17

请查看http://docs.djangoproject.com/en/dev/ref/forms/validation/#ref-forms-validation

其次,一旦我们决定两个字段中的组合数据无效,我们必须记得将它们从cleaned_data中删除。

实际上,如果表单中有任何错误,Django当前将完全清除cleaned_data字典。但是,这种行为可能会在未来发生变化,因此最好首先自己做好清理工作。

原始数据始终可在request.POST中使用。


评论建议要执行听起来更复杂的字段级验证。

每个字段都提供未经验证的数据,然后返回有效数据或引发异常。

在每个字段中,可以对原始内容进行任何类型的验证。


我希望有更好的方法,因为那里有原始、混乱的输入名称,是表单集合制作的,例如form-1-my_input。所以我必须进行一些字符串扭曲来获取正确表单的数据。 - Greg
2
我认为OP的意思是,即使表单没有通过验证,也可以操纵表单解析的内容:form.phone_number将返回字段phone_number得到的任何值,而不管它是否正确验证。 - Carl G
1
无效的数据?我不明白。数据要么有效且可以被处理,要么无效。如果您想放宽规则以允许更多内容(并进行其他清理),那就需要进行字段级别的验证。一旦完成了字段级别的验证,它要么有效,要么整个表单都是无用的。 - S.Lott

14

我曾经遇到类似的问题,在这里找到了一个很好的讨论:https://code.djangoproject.com/ticket/10427

虽然文档不够清晰,但是对于一个实时表单,你可以通过以下方法查看字段值 -- 此处所见即所得:

form_name['field_name'].value()

这对我来说比@Dmitry的答案更好,因为在模板中,MultipleHiddenInput可以使用form.data.field_name.返回一个项目,而form.field_name.value则返回整个列表。 - augustomen
有没有办法在 is_valid() 为 false 的情况下更新这个字段? - KVISH
这样做还可以,但对于表单集并不适用,因为field_name类似于“form_0_field_name”,因此在没有cleaned_data的情况下并不理想。在这种情况下,您必须先清除表单。有更好的方法吗? - radtek

11

我有很多方法,你可以选择任意一种。

我认为表单应该像下面这样:

class SignupForm(forms.Form):
    email = forms.CharField(label='email')
    password = forms.CharField(label='password',
                               widget=forms.PasswordInput)

1-1. 获取来自request的数据

def signup(req):
    if req.method == 'POST':
        email = req.POST.get('email', '')
        password = req.POST.get('password', '')

2-1. 获取分配给字段的原始值,并返回字段data属性的值

def signup(req):
    if req.method == 'POST':
        ...
        sf = SignupForm(req.POST)
        email = sf["email"].data
        password = sf["password"].data
        ...

2-2. 获取分配给字段的原始值并返回字段value属性的值。

def signup(req):
    if req.method == 'POST':
        ...
        sf = SignupForm(req.POST)
        email = sf["email"].value()
        password = sf["password"].value()
        ...

2-3. 获取分配给字段的 字典

def signup(req):
    if req.method == 'POST':
        ...
        sf = SignupForm(req.POST)
        # print sf.data
        # <QueryDict: {u'csrfmiddlewaretoken': [u'U0M9skekfcZiyk0DhlLVV1HssoLD6SGv'], u'password': [u''], u'email': [u'hello']}>
        email = sf.data.get("email", '')
        password = sf.data.get("password", '')
        ...

5

您可以使用这个模式:

class MyForm(forms.Form):
    ...
    def clean(self):
        self.saved_data=self.cleaned_data
        return self.cleaned_data

在你的代码中:

if form.is_valid():
    form.save()
    return django.http.HttpResponseRedirect(...)
if form.is_bound:
    form.saved_data['....'] # cleaned_data does not exist any more, but saved_data does.

使用form.data不是一个好的解决方案。原因如下:

  • 如果表单有前缀,字典键将带有这个前缀。
  • form.data中的数据没有经过清理:只有字符串值。

4
我使用表单集遇到了类似的问题。在我的例子中,我希望用户在选择第二个选项之前先选择第一个选项,但如果第一个选项出现其他错误,则“在选择第二个选项之前选择第一个选项”错误也会显示。
为了获取第一个字段的未清理数据,我在表单字段的清理方法中使用了以下代码:
dirty_rc1 = self.data[self.prefix + '-reg_choice_1']

然后,我可以测试该字段中是否存在数据:

if not dirty_rc1:
    raise ValidationError('Make a first choice before second')

希望这能帮到您!

1

你可以从字段的clean()方法或表单的clean()方法中访问数据。clean()是确定表单是否有效的函数。当调用is_valid()时,它会被调用。在表单的clean()中,你有一个cleaned_data列表,你可以通过自定义代码来确保它全部检查通过。在小部件中,你也有一个clean(),但它使用一个单独传递的变量。为了访问字段的clean()方法,你需要对其进行子类化。例如:

class BlankIntField(forms.IntegerField):
    def clean(self, value):
        if not value:
            value = 0
        return int(value)

如果您想要一个不会因为空值而出错的IntField,例如,您可以使用上面的代码。
在表单中,clean() 的工作方式类似于以下内容:
def clean(self):
    if self.cleaned_data.get('total',-1) <= 0.0:
        raise forms.ValidationError("'Total must be positive")
    return self.cleaned_data

此外,您可以为每个字段编写一个clean_FIELD()函数,以便在调用字段的clean()函数后单独验证每个字段。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接