zoukankan      html  css  js  c++  java
  • 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()函数,什么也没做,可以自己写验证,但是用得较少,而且在源码中没有做任何的异常处理。

  • 相关阅读:
    3.Appium运行时出现:Original error: Android devices must be of API level 17 or higher. Please change your device to Selendroid or upgrade Android on your device
    3.Python连接数据库PyMySQL
    2.Python输入pip命令出现Unknown or unsupported command 'install'问题解决
    2.Linux下安装Jenkins
    5.JMeter测试mysql数据库
    Android 4学习(7):用户界面
    Android 4学习(6):概述
    Android 4学习(5):概述
    Android 4学习(4):概述
    Android 4学习(3):概述
  • 原文地址:https://www.cnblogs.com/zjsthunder/p/9817217.html
Copyright © 2011-2022 走看看