验证自定义小部件

时间:2019-09-17 08:10:30

标签: python django

在小部件输入本身可能不一致的自定义小部件中,应该如何传播验证错误?举例来说,我正在为Date字段创建一个自定义日期输入小部件,该字段允许用户根据日文英制日历选择日期。这需要一个时代下拉列表和一个年份输入,并且完全有可能选择一个本身无效的时代-年份组合。小部件使用date / MultiWidget.value_from_datadict方法将此输入与Python MultiWidget.decompress对象之间的输入转换为

def value_from_datadict(self, data, files, name):
    era, imperial_year, month, day = [widget.value_from_datadict(data, files, f'{name}_{i}')
                                      for i, widget in enumerate(self.widgets)]

    try:
        return date(self._j2w.convert(f'{era}{imperial_year}年'), int(month), int(day))
    except ValueError:
        # selected era/year combination was invalid
        return ''

我在此方法中所能做的就是捕获任何ValueError并返回一个空值,这意味着字段的验证程序抱怨缺少数据,而不是错误的值。如果我只是raise ValueErrorValidationError,则会导致未捕获的异常错误。

在哪里以及如何进行这种验证?我希望将日语选择器的抽象完全保留在UI层内,并将后备字段保留为简单的Date字段。

1 个答案:

答案 0 :(得分:0)

目前,我已经通过以下方式解决了该问题:

widgets.py

from django.core.exceptions import ValidationError
from django.forms import MultiWidget

class ImperialDateInput(MultiWidget):
    def value_from_datadict(self, data, files, name):
        try:
            ...
        except ValueError:
            return ValidationError(
                _('Invalid date: %(era)s%(year)s/%(month)s/%(day)s'),
                code='invalid',
                params=dict(era=era, year=imperial_year, month=month, day=day)
            )

fields.py

from django.core.exceptions import ValidationError
from django.forms import DateField
from .widgets import ImperialDateInput

class ImperialDateField(DateField):
    widget = ImperialDateInput

    def to_python(self, value):
        if isinstance(value, ValidationError):
            raise value

        return super().to_python(value)

models.py

from django.db import models
from . import fields

class ImperialDateField(models.DateField):
    def formfield(self, **kwargs):
        return super().formfield(form_class=fields.ImperialDateField, **kwargs)


class MyModel(models.Model):
    date = ImperialDateField()

换句话说,小部件的value_from_datadict将从定制输入元素转换为常规date,或者返回 ValidationError的实例。这由一个特殊的“表单字段”作为后盾,该表单字段在其to_python方法中识别出此情况,并允许从该地方raise来处理该异常。为了将这种权限引入模型,我还添加了一个自定义模型字段,但这是可选的。

不确定这是否是解决此问题的最优雅的方法;确实有点恶心。