【问题标题】:Django dynamic forms - validating Select fieldDjango 动态表单 - 验证 Select 字段
【发布时间】:2020-11-27 16:44:54
【问题描述】:

我正在使用 Django 2.2

我正在通过读取 JSON 定义文件动态创建表单;配置文件指定小部件的类型、允许的值等。

但是,我对 Select 小部件有点不满意,因为我在列表中的允许值列表前添加了一个“--”(这样我就会知道用户何时没有选择项目)。

这是创建 Select 小部件的代码 sn-p:

class myForm(forms.Form):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

       # ...
       elif widget_type_id == WIDGET_TYPE_DROPDOWN:
            CHOICES.insert(0,('-', INVALID_SELECTION))
            form_field = CharField(label=the_label, widget=Select(choices=CHOICES),required=is_required)

我的问题是,当在我的表单上调用 is_valid() 方法时,无论选择如何,任何呈现的 Select 小部件都被接受为有效。

我想实现具有这种逻辑的代码(下面的伪代码):

def is_valid(self):
    for field_name, field in self.fields.items():
        if isinstance(field.type, Select):
            if field.required and field.selected_value == INVALID_SELECTION:
                return False

     return super().is_valid()

实现此功能的正确方法是什么?例如,我什至如何获取字段的选定值(在表单代码中)?

【问题讨论】:

  • 如果你使用(None, INVALID_SELECTION)会怎样? None 而不是 -
  • 可能您可以为选择字段动态添加clean_field_name 方法并覆盖验证行为。

标签: django django-forms


【解决方案1】:

验证表单数据:docs

  • 通过创建带有验证的自定义Field
  • 使用fieldname_clean()的特定字段
  • 通过覆盖clean()
  • 使用验证器

实现此功能的正确方法是什么?
我与你所问的伪代码有些偏差

验证器 docs

已经有很多buitin验证器。为此,创建一个自定义验证器来检查字段中的值并引发ValidationError

在您的应用中创建验证器.py

# validator.py
from django.core.exceptions import ValidationError

def validate_select_option(value):
    if value == '-':
        raise ValidationError('Invalid selection')

在 forms.py 中导入 validate_select_option 并将验证器添加到该字段

# forms.py
from .validator import validate_select_option

# other parts of code

form_field = CharField(label=the_label, widget=Select(choices=CHOICES), required=is_required, validator=[validate_select_option])

这个验证器validate_select_option 现在不仅可以用于有选择的字段,还可以用于任何其他要验证的字段。总之,这不是它的本意。所以可以用于任何有选择的字段:)

既然您说您正在动态创建表单,我假设您可以使用 JSON 定义文件向 Field 添加其他选项。对于fieldname_clean()clean(),您需要在Form 类中添加这些方法。可以创建自定义字段并将其导入。但是,我认为简单的验证器可以轻松做到这一点。


我什至如何获取字段的选定值(在表单代码中)

如果使用Form类方法clean(self, *args, **kwargs)fieldname_clean(self, *args, **kwargs):可以通过self.cleaned_data字典访问表单数据。 cleaned_data 仅在 is_valid() 之后创建。

在覆盖 is_valid 时,为了访问表单数据,我们需要先调用父验证,然后再进行处理。

def is_valid(self, *args, **kwargs):
    # cannot access self.cleaned_data since it is not created yet

    valid = super(myForm, self).is_valid()

    # can access self.cleaned_data since it has been created when parent is_valid() has been called

    for fieldname, field in self.fields.items():
        if isinstance(field, forms.CharField): # the type of widget is not considered.
            if fieldname in self.cleaned_data and field.required and self.cleaned_data[fieldname] == '-':
                valid = False

    return valid

【讨论】:

    【解决方案2】:

    它有点隐藏,因为您使用的是 CharFieldSelect 小部件,但如果您查看 ChoiceField 文档,它说空值应该是一个空字符串。

    假设表单字段是required=True,您应该能够将您的空值元组更改为('', INVALID_SELECTION)

    【讨论】:

      【解决方案3】:

      CHOICES 已有值。我插入 ('INVALID_SELECTION', 'INVALID_SELECTION') 这个。并检查form_field 字段的值是否为INVALID_SELECTION,然后在同一字段中添加错误。提交其他表单。

      views.py

      class DynamicFormView(View):
          template_name = 'test.html'
          def get(self, request, *args, **kwargs):
              form = myForm( request.POST or None)
              context = { 
                  'form' : form,
              }
              return render(request, self.template_name, context)
          
          def post(self, request,id = None, *args, **kwargs):
              context = {}
              form = myForm(request.POST or None,)
              if form.is_valid():
                  if request.POST['form_field'] == 'INVALID_SELECTION':
                      form.add_error("form_field",_("This field is required."))
                  else:
                      form.save()
              context = { 
                  'form' : form, 
              }
              return render(request, self.template_name, context)
      

      forms.py

      class myForm(forms.Form):
          CHOICES = [
              ('FR', 'Freshman'),
              ('SO', 'Sophomore'),
              ('JR', 'Junior'),
              ('SR', 'Senior'),
              ('GR', 'Graduate'),
          ]
          CHOICES.insert(0,('INVALID_SELECTION', 'INVALID_SELECTION'))
          form_field = forms.ChoiceField(label='the_label',choices=CHOICES,widget=forms.Select(attrs={'class':' form-control'}),required = False)
          def __init__(self, *args, **kwargs):
              super().__init__(*args, **kwargs)
      
          def save(self):
              # form is success
              pass
          class Meta:
              model   = Student
      

      urls.py

      path('dyanmic-form/create/',  views.DynamicFormView.as_view(), name='dyanamic_form_create'),
      

      对不起,我的英语不好。

      【讨论】:

      • 你试试这个吗??
      • 我更新了我的答案,请试试这个 - Homunculus Reticulli
      猜你喜欢
      • 2016-04-08
      • 2014-02-22
      • 1970-01-01
      • 1970-01-01
      • 2017-12-09
      • 2021-08-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多