检查错误和其他值在小部件级别 - 也许使用自定义表单字段

时间:2019-02-02 09:25:04

标签: django django-forms django-widget

如何访问如果字段具有)在窗口小部件?

的电平误差

我尝试使用默认设置:

{% if widget.attributes.has_errors %} or {% if widget.has_errors %}

但不起作用。

我使用自定义窗口小部件模板,我正在考虑使用自定义表单字段,并覆盖默认字段。

我知道存在clean方法,但是我不知道如何将所需的动态(非默认)数据/属性推送到小部件。

我尝试过:

class AWidget(forms.Widget):

    def get_context(self, name, value, attrs):

        context = super().get_context(name, value, attrs)
        has_errors = context['widget']['attrs'].pop('has_errors', None)
         context['widget']['has_errors'] = has_errors

它适用于errors,但是我不知道这是否是最好的选择,而且我想从Form Field传递其他值/属性,我认为尝试覆盖Form Field会更好。不知道究竟如何。

还可以使用以下方法访问各个属性:

 {{ widget.attrs.maxlength }} or  {{ widget.attrs.items.maxlength }}

即使加入for循环也可以


我知道我可以添加带有一类错误的父div:

 <div class="{% if form.field.errors %}pass_error{% endif %}">
        {{ form.field }} 
    </div>

但是,使得在CSS水平意味着大的变化。

我已经用自定义小部件覆盖了所有Django小部件,因为错误,我不需要仅更改边框颜色,而是显示或不显示小部件模板的不同元素以及某些位置他们改变了。

我已经修改了基于基础的窗口小部件以添加错误,但是我希望通过从字段到窗口小部件的传递(取决于错误类型)来以一种更优雅的方式在字段级别上做到这一点。

所以我的问题是我需要重写什么才能从字段传递到小部件错误和其他变量?

2 个答案:

答案 0 :(得分:3)

好吧一个小部件是如何将字段的数据/值渲染到HTML渲染的模板中,这是小部件的唯一功能,请看以下摘自文档的示例:

>>> name = forms.TextInput(attrs={'required': True})
>>> name.render('name', 'A name')
'<input name="name" type="text" value="A name" required>'
>>>
>>> name = forms.TextInput(attrs={'required': False})
>>> name.render('name', 'A name')
'<input name="name" type="text" value="A name">'

因此,小部件不知道数据是否有效(有错误),应该保持这种状态。

在窗口小部件级别处理任何数据错误/验证不是一个好主意,我想确保,如果您更改字段(窗口小部件)的外观,则验证将继续有效。

说...

如何访问字段错误?

呈现表单时,您可以逐字段进行操作,让我们以该表单为例:

class LoginForm(forms.Form):
    username = forms.CharField(max_length=255)
    password = forms.CharField(widget=forms.PasswordInput)

您可以写到temlate:

<form action="." method="get">
   <p>{{ loginform.username.label }}: {{ loginform.username }}</p>
   <p>{{ loginform.password.label }}: {{ loginform.password}}</p>
    <button type="submit">submit</button>
</form>

这将呈现如下内容:

enter image description here

现在,假设您的表单不接受少于8个字符的密码:

class LoginForm(forms.Form):
    username = forms.CharField(max_length=255)
    password = forms.CharField(widget=forms.PasswordInput)

    def clean_password(self):
        password = self.cleaned_data['password']
        if len(password) < 8:
            raise forms.ValidationError(
            "Password must have at least 8 characters, it has only %(password_length)s",
            code='invalid password',
            params={'password_length': len(password)}
        )
        return password

您可以访问密码错误,如下所示:

<form action="." method="get">
    {% csrf_token %}
    <p>{{ form.username.label }}: {{ form.username }}</p>
    <p>{{ form.password.label }}: {{ form.password}}</p>

    <ul>
        {% for error in form.password.errors %}
            <li>{{ error }}</li>    
        {% endfor %}
    </ul>

   <button type="submit">submit</button>
</form>

现在,如果您输入短密码...

enter image description here

如果有错误,我希望控件看起来与众不同。

如果有错误,您可以添加一些样式,只需在模板代码中使用{% if ... %}

 <p>
    {{ form.password.label }}:
    <span class="{% if form.password.errors %}pass_error{% endif %}">
        {{ form.password }} 
    </span>
 </p>

使用CSS:

 <style>
    .pass_error input {
        border-color: red;
    }
 </style>

这是结果:

enter image description here

结论。

以表格或使用validators的形式验证和处理数据错误,使用小部件显示数据,当然,由于可以为小部件指定自定义模板,因此您可以自定义数据的显示方式。

如果您想在模板代码中向小部件添加属性,我也建议django-widget-twaeks。这个应用程式可让您编写类似(例如来自应用程式文件的范例)的程式码:

{% load widget_tweaks %}

<!-- change input type (e.g. to HTML5) -->
{% render_field form.search_query type="search" %}

<!-- add/change several attributes -->
{% render_field form.text rows="20" cols="20" title="Hello, world!" %}

<!-- append to an attribute -->
{% render_field form.title class+="css_class_1 css_class_2" %}

<!-- template variables can be used as attribute values -->
{% render_field form.text placeholder=form.text.label %}

答案 1 :(得分:3)

不确定这是否对您的特定用例有所帮助...但是以防万一,请注意,在视图中构建表单时,可以根据需要添加其他参数,然后将其传递给自定义小部件

工作示例:

文件“ forms.py”

from django import forms


def build_ingredient_form(unit):
    """
    Ingredient form factory

    Here we build the form class dynamically, in order to acces 'unit' via closure.
    References:
        http://stackoverflow.com/questions/622982/django-passing-custom-form-parameters-to-formset#623030
    """
    class IngredientForm(forms.Form):
        #quantity = forms.DecimalField(max_digits=10)
        quantity = UnitField(unit, required=False)
        ...
    return IngredientForm


文件“ fields.py”

from django import forms
from .fields import UnitField


class UnitField(forms.CharField):
    """
    Custom field to support UnitWidget

    References:
        - http://tothinkornottothink.com/post/10815277049/django-forms-i-custom-fields-and-widgets-in
    """

    def __init__(self, unit, *args, **kwargs):
        self.unit = unit
        super(UnitField, self).__init__(*args, **kwargs)
        self.widget = UnitWidget(unit)

    ...

文件“ widgets.py”

from django import forms
from .models import Unit


class UnitWidget(forms.TextInput):

    def __init__(self, unit, attrs=None):
        if unit is None:
            self.unit = Unit()
        else:
            self.unit = unit

    ...