• Django中的form组件


    Django中的form组件有两大作用

    1、验证获取正确的结果或者错误信息

    2、生成html代码

    一、为什么需要form组件呢?

    在写form表单,提交数据时,自己写验证的代码是一件非常困难的事情。

    复制代码
    <form action="/test/"method="post">
        {% csrf_token %}
        <input type="text"name="user">
        <input type="password"name="password">
        <input type="email"name="email">
        <input type="submit" value="提交">
    </form>
    复制代码

    上述表单提交后,在下面的视图中需要自己处理对提交数据的限制,非常的困难,而form组件能够实现对表单提交的数据进行验证的功能,也支持ajax提交的数据验证,错误是能获取错误信息。

    复制代码
    def test(request):
        if request.method =="GET":
            return render(request,"test.html")
        elif request.method == "POST":
            user = request.POST.get("user")
            password = request.POST.get("password")
            email = request.POST.get("email")
            print(user,password,email)
            return HttpResponse("ok")
    复制代码

    二、form组件的创建

    先导入forms和fields,创建类和字段。字段中的参数和封装的正则表达式对传入进来的数据进行验证。将post传入进来的数据传入类中实例化生成一个对象,通过方法

    is_valid(),可以拿到正确的数据或者错误的信息。
    复制代码
    from django import forms
    from django.forms import fields
    
    
    class MyForm(forms.Form):
        user = fields.CharField(max_length=12,min_length=3,required=True)
        password = fields.CharField(max_length=32,required=True)
        email = fields.EmailField(required=True)
    复制代码
    复制代码
            form_obj = MyForm(request.POST)
            if form_obj.is_valid():
                # 验证成功后拿到的数据
                print(form_obj.cleaned_data)
            else:
                # 验证失败后,拿到的错误信息
                print(form_obj.errors)
    复制代码

     三、验证错误信息显示

    复制代码
    <form action="/test/"method="post">
        {% csrf_token %}
        <p><input type="text"name="user">{{ form_obj.errors.user.0 }}</p>
        <p><input type="password"name="password">{{ form_obj.errors.password.0 }}</p>
        <p><input type="email"name="email">{{ form_obj.errors.email.0 }}</p>
        <p><input type="submit" value="提交"></p>
    </form>
    复制代码
    复制代码
    class MyForm(forms.Form):
        user = fields.CharField(max_length=12,min_length=3,required=True,error_messages={
            "required":"用户不能为空",
            "max_length":"用户名长度不能超过12个字符",
            "min_length":"用户名长度不能短于3个字符",
        })
        password = fields.CharField(max_length=32,required=True,error_messages={
            "max_length":"密码长度不能大于32个字符",
            "required":"密码不能为空",
        })
        email = fields.EmailField(required=True,error_messages={
            "required":"邮箱不能为空",
            "invalid":"请输入邮箱格式",
        })
    复制代码

    error_message可以将错误信息定制为中文信息。

    四、生成html代码

    用form组件生成html代码有两个好处,1、当和其他人合作开发时,前端的form表单命名和后端form组件的字段名字不一样时,会出错,当采用form组件自动生成html代码不会存在名字不同的情况。2、可以保留上次输入的信息。

    复制代码
    <form action="/test/"method="post">
        {% csrf_token %}
        <p>{{ form_obj.user }}{{ form_obj.errors.user.0 }}</p>
        <p>{{ form_obj.password }}{{ form_obj.errors.password.0 }}</p>
        <p>{{ form_obj.email }}{{ form_obj.errors.email.0 }}</p>
        <p><input type="submit" value="提交"></p>
    </form>
    复制代码
    复制代码
    def test(request):
        if request.method =="GET":
            form_obj = MyForm()
            return render(request,"test.html",locals())
        elif request.method == "POST":
            form_obj = MyForm(request.POST)
            if form_obj.is_valid():
                # 验证成功后拿到的数据
                print(form_obj.cleaned_data)
            else:
                # 验证失败后,拿到的错误信息
                print(form_obj.errors)
                return render(request,"test.html",locals())
            return HttpResponse("ok")
    复制代码

    验证后通过form_obj.cleaned_data拿到的数据为字典,键为form中的字段名,因此当需要通过models.User.objects.create(**form_obj.cleaned_data)方法实现增加数据库中的数据时,需要足以保持model中和form中的字段名称相同。不同时,需要一个一个的传入字段。

    五、整体结构

    1、字段

    字段的作用是提供验证时的格式要求,如果不符合格式要求则在error_message中的invalid定制错误信息。字段包含两样东西,验证的规则,生成html代码的wiget。

    2、参数

    参数实现对验证规则的正则表达式的定制。其中最重要的参数wiget实现对生成html代码的定制,可以生成不同的输入框(如select,Ridio,CheckBox),还能实现对生成的代码添加属性

    3、一些常用实例

    select,Ridio,CheckBox

    ridio没有直接的字段,可以用其他的字段加上widget生成,一般经常选择的字段有CharField,ChoiceField,IntegerField

    下面字段生成的html代码如下。

    复制代码
        test = fields.ChoiceField(
            required=True,
            choices=((1,"重庆"),(2,"成都"),),
            widget=widgets.RadioSelect()
        )
    test = fields.CharField(#用IntegerField一样
    required=True,
    widget=widgets.RadioSelect(choices=((1,"重庆"),(2,"成都"),),)
    )
     
    复制代码
    复制代码
    <ul id="id_test">
        <li><label for="id_test_0"><input type="radio" name="test" value="1" required="" id="id_test_0">
     重庆</label>
    
    </li>
        <li><label for="id_test_1"><input type="radio" name="test" value="2" required="" id="id_test_1">
     成都</label>

    </li> </ul>
    复制代码

    select

        test = fields.IntegerField(
            required=True,
            widget=widgets.Select(choices=((1,"重庆"),(2,"成都"),),),
            label="city"
        )
    复制代码
        test = fields.ChoiceField(
            required=True,
            choices=((1, "重庆"), (2, "成都"),),
            widget=widgets.Select(),
            label="city"
        )
    复制代码

    生成的html代码如下

    复制代码
    <select name="test" id="id_test">
      <option value="1">重庆</option>
    
      <option value="2">成都</option>
    
    </select>
    复制代码

    select多

    复制代码
        test = fields.ChoiceField(
            required=True,
            choices=((1, "重庆"), (2, "成都"),(3,"上海")),
            widget=widgets.SelectMultiple(),
            label="city"
        )
    复制代码

    产生的html代码如下:

    复制代码
    <select name="test" required="" id="id_test" multiple="multiple">
      <option value="1">重庆</option>
    
      <option value="2">成都</option>
    
      <option value="3">上海</option>
    
    </select>
    复制代码

    checkbox单

        test = fields.ChoiceField(
            required=True,
            widget=widgets.CheckboxInput(),
            label="city"
        )
    <p>city<input type="checkbox" name="test" required="" id="id_test"></p>

    checkbox多

    复制代码
        test = fields.ChoiceField(
            required=True,
            choices=((1,"重庆"),(2,"成都"),(3,"上海")),
            widget=widgets.CheckboxSelectMultiple(),
            label="city"
        )
    复制代码
    复制代码
    <ul id="id_test">
        <li><label for="id_test_0"><input type="checkbox" name="test" value="1" id="id_test_0">
     重庆</label>
    
    </li>
        <li><label for="id_test_1"><input type="checkbox" name="test" value="2" id="id_test_1">
     成都</label>
    
    </li>
        <li><label for="id_test_2"><input type="checkbox" name="test" value="3" id="id_test_2">
     上海</label>
    
    </li>
    </ul>
    复制代码

     

     六、select选择数据库中的数据时,采用上面的创建类和字段的方法不能实现实时更新(即往数据库中添加数据,页面的下拉选择就能够出现更新的内容)

    这是因为上述创建的字段为静态字段,在程序在载时加载后就不在执行了。可以采用如下的方式解决该问题。

    复制代码
        test_user = fields.ChoiceField(
            required=False,
            choices=models.User.objects.values_list("id","user")
        )
    
        def __init__(self,*args,**kwargs):
            super(MyForm, self).__init__(*args,**kwargs)
            self.fields["test_user"].widget.choices = models.User.objects.values_list("id","user")
    复制代码

    将字段在初始化函数中再次赋值。

    另外一种解决方法是:用ModelChoiceField字段

    from django.forms.models import ModelChoiceField
        test_user = ModelChoiceField(
            queryset=models.User.objects.all(),
            to_field_name="user"
        ) # to_field_name="user"指定生成option的html代码的value的值。

    但是这个方法得到的下拉框中内容是一个对象,要是名字要用__str__方法。并且扩展性不强。

    七、对数据的验证过程和验证扩展

    对数据的验证是从is_valid开始,

        def is_valid(self):
            """Return True if the form has no errors, or False otherwise."""
            return self.is_bound and not self.errors
    复制代码
        @property
        def errors(self):
            """Return an ErrorDict for the data provided for the form."""
            if self._errors is None:#没有错误时,继续验证,在full_clean中验证
                self.full_clean()
            return self._errors
    复制代码
    复制代码
        def full_clean(self):
            """
            Clean all of self.data and populate self._errors and self.cleaned_data.
            """
            self._errors = ErrorDict()
            if not self.is_bound:  # Stop further processing.
                return
            self.cleaned_data = {}
            # If the form is permitted to be empty, and none of the form data has
            # changed from the initial data, short circuit any validation.
            if self.empty_permitted and not self.has_changed():
                return
    
            self._clean_fields()
            self._clean_form()
            self._post_clean()
    复制代码
    复制代码
        def _clean_fields(self):
    #name为字段名,field是具体的字段 for name, field in self.fields.items(): # value_from_datadict() gets the data from the data dictionaries. # Each widget type knows how to retrieve its own data, because some # widgets split data over several HTML fields. if field.disabled: value = self.get_initial_for_field(field, name) else: value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name)) try: if isinstance(field, FileField): initial = self.get_initial_for_field(field, name) value = field.clean(value, initial) else: value = field.clean(value) self.cleaned_data[name] = value
    #上述是进行正则表达式的验证,当正则表达式验证通过时,将值传递到self.cleaned_data中。下面是留的验证扩展的接口 if hasattr(self, 'clean_%s' % name): value = getattr(self, 'clean_%s' % name)() self.cleaned_data[name] = value except ValidationError as e: self.add_error(name, e)
    复制代码

    扩展时的类中函数名为clean_%s%name,函数要具有返回值;出错时,函数抛出ValidationError 异常。

    如果存在较多的字段需要同时验证,在self._clean_form()中,

    复制代码
        def _clean_form(self):
            try:
                cleaned_data = self.clean()#源代码中该函数没有执行任何操作,直接返回clean_data,因此源代码没有做任何处理
            except ValidationError as e:
                self.add_error(None, e)
            else:
                if cleaned_data is not None:
                    self.cleaned_data = cleaned_data
    复制代码
    复制代码
        def clean(self):
            """
            Hook for doing any extra form-wide cleaning after Field.clean() has been
            called on every field. Any ValidationError raised by this method will
            not be associated with a particular field; it will have a special-case
            association with the field named '__all__'.
            """
            return self.cleaned_data
    复制代码

    在新建的类中,重写clean方法,可以实现对多个字段的验证,验证通过返回验证后的数据,出错时,抛出ValidationError 异常。捕捉到的异常加到错误信息中的键为__all__。

    复制代码
        def _post_clean(self):
            """
            An internal hook for performing additional cleaning after form cleaning
            is complete. Used for model validation in model forms.
            """
            pass
    复制代码

    还有一个_post_clean()函数,什么也没做,可以自己写验证,但是用得较少,而且在源码中没有做任何的异常处理。

  • 相关阅读:
    JavaScript 常见面试题
    textarea 元素的 placeholder 属性不显示
    CSS 画一个八卦
    CSS 画一个心
    JS判断客户端是否是iOS或者Android端
    前端面试题(一)
    选中文字改变默认颜色
    红包雨的实现
    template 的使用
    函数和入参
  • 原文地址:https://www.cnblogs.com/zjsthunder/p/9817217.html
Copyright © 2020-2023  润新知