【问题标题】:Where has cleaned_data vanished in Django 1.11?在 Django 1.11 中,cleaned_data 在哪里消失了?
【发布时间】:2017-04-10 10:19:03
【问题描述】:

我创建了一个 inlineformset_factory 如下:

formset = inlineformset_factory(Author, Book, form=BookForm,
                                formset=BaseBookFormSet,
                                can_order=False, can_delete=True,
                                extra=1, fields=('id', name)
                                )

BookForm如下:

class BookForm(forms.ModelForm):
    name = forms.Charfield(required=True)

    def __init__(self, *args, **kwargs):
        super(BookForm, self).__init__(*args, **kwargs)
        self.helper = FormHelper()
        self.helper.form_tag = False
        self.helper.layout = Layout(
            Div(
                Field("id", type="hidden"),
                Field("name"),
                Field("DELETE")
                )
    )

    class Meta:
        model = Book
        fields = ('id', 'name')

    def clean_name(self):
        book_name = self.cleaned_data['name']
        try:
            book = Book.objects.get(name=book_name)
            return book
         except:
            return book_name

    def clean(self):
        cleaned_data = super(BookForm, self).clean()
        ... other operations on cleaned_data ...

    def has_changed(self):
        changed = super(BookForm, self).has_changed()
        cleaned_data = self.clean()
        ... other code here ...

这是在提交表单时引发错误:

Exception Type: AttributeError
Exception Value: 'BookForm' object has no attribute 'cleaned_data'

当在 views.py 中调用 formset.is_valid() 时。 Traceback 首先显示 has_changed 中调用 self.clean 的行,然后显示 clean() 中调用 super clean 的行。

这曾经在 django 1.10 中运行良好。

当我尝试在 Django 1.10 中打印 dir(self) 时,它确实将“cleaned_data”显示为属性之一,而在 Django 1.11 中却没有。

Django 1.11 中的“cleaned_data”在哪里消失了?

编辑:添加回溯:

Traceback (most recent call last):
  File "/home/vagrant/venv/local/lib/python2.7/site-packages/django/core/handlers/exception.py", line 41, in inner
    response = get_response(request)
  File "/home/vagrant/venv/local/lib/python2.7/site-packages/django/core/handlers/base.py", line 249, in _legacy_get_response
    response = self._get_response(request)
  File "/home/vagrant/venv/local/lib/python2.7/site-packages/django/core/handlers/base.py", line 187, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "/home/vagrant/venv/local/lib/python2.7/site-packages/django/core/handlers/base.py", line 185, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/home/vagrant/venv/local/lib/python2.7/site-packages/django/views/generic/base.py", line 68, in view
    return self.dispatch(request, *args, **kwargs)
  File "/home/vagrant/venv/local/lib/python2.7/site-packages/django/views/generic/base.py", line 88, in dispatch
    return handler(request, *args, **kwargs)
  File "/vagrant/test_os/inventory/views.py", line 297, in post
    if formset.is_valid():
  File "/home/vagrant/venv/local/lib/python2.7/site-packages/django/forms/formsets.py", line 321, in is_valid
    self.errors
  File "/home/vagrant/venv/local/lib/python2.7/site-packages/django/forms/formsets.py", line 295, in errors
    self.full_clean()
  File "/home/vagrant/venv/local/lib/python2.7/site-packages/django/forms/formsets.py", line 345, in full_clean
    if not form.has_changed():
  File "/vagrant/test_os/inventory/forms.py", line 220, in has_changed
    cleaned_data = self.clean()
  File "/vagrant/test_os/inventory/forms.py", line 177, in clean
    cleaned_data = super(BookForm, self).clean()
  File "/home/vagrant/venv/local/lib/python2.7/site-packages/django/forms/models.py", line 344, in clean
    return self.cleaned_data
AttributeError: 'BookForm' object has no attribute 'cleaned_data'

【问题讨论】:

  • 请显示完整的回溯。 super(StockForm, self) 是错字吗?
  • 是的,StockForm 是一个错字。添加了回溯。

标签: python django formset inline-formset


【解决方案1】:

Formsets 在 1.11 (in #26844) 中得到修复,以在验证最小表单数时忽略空表单。作为副作用,表单集现在在验证表单之前在每个表单上调用 form.has_changed()。 Django 期望form.has_changed() 在表单验证之前可以安全调用,默认实现确实可以安全调用。

您已覆盖 form.has_changed() 以调用 self.clean(),这现在发生在表单验证之前。由于form.clean() 要求验证表单,因此失败。

由于form.full_clean() 实际调用self.has_changed(),您不能简单地从form.has_changed() 中验证表单。你没有在has_changed() 中展示你所做的事情,但将这段代码放在其他地方很可能是个好主意。

【讨论】:

  • 我正在使用 inlineformset_factory 以及编辑视图 CreateView 和 UpdateView。因此,当我在 UpdateView 中初始化表单集时,它会显示外键的 ID 而不是内联表单中的名称。因此,在 BookForm init 中,我使用名称填充该字段。 if self.instance.id and not self.data: name = self.instance.name if name: self.initial['sku'] = name Django 将此作为更改的数据并将 has_changed 标记为 True。所以在has_changed中,我检查名称和对应的ID是否匹配,将has_changed标记为False。
  • 在这种情况下,请查看source code of changed_data(),了解如何在表单验证之前访问数据。
  • 好的,解决了 - 在 has_changed 方法中将 self.cleaned_data 包含在 try 块中并传递 except
猜你喜欢
  • 2014-01-28
  • 1970-01-01
  • 1970-01-01
  • 2023-03-03
  • 2018-10-25
  • 1970-01-01
  • 1970-01-01
  • 2023-01-10
  • 2015-07-28
相关资源
最近更新 更多