zoukankan      html  css  js  c++  java
  • Form组件使用

    form组件

    form组件的功能

    • 生产input标签
    • 对提交的数据进行校验
    • 提供错误提示

    form组件中常用字段

    # 查看所有字段可点进forms中,fields里面是所有字段,widgets是所有插件
    
    CharField    # 生成input框,默认文本输入框
    ChoiceField   # 选择框,默认是select单选下拉框
    MultipleChoiceField	# 多选框,默认是select下拉框。里面是选择的内容
    
    注:想更改可以用已经写好的字段,也可以直接改widget
    如:email = forms.CharField(label='邮箱',widget=EmailInput)将文本输入框改成了邮箱输入框。
    

    字段参数

    required=True,               是否允许为空
    widget=None,                 HTML插件,改变input框中格式,如:从文本变成email
    label=None,                  用于生成Label标签或显示内容
    initial=None,                初始值,默认值,默认填在input框中
    error_messages=None,         修改错误信息显示内容 {'required': '不能为空', 'invalid': '格式错误'}
    						   键是对应的参数,值是想要显示的错误信息内容
    validators=[],               自定义验证规则
    disabled=False,              是否可以编辑
    min_length:	                 设置最小长度
        
    # widget=forms.PasswordInput()	    设置成密文
    # widget=forms.RadioSelect()		设置单选框
    # widget=CheckboxInput()		设置input选择框,应用:记住密码
    # widget=forms.CheckboxSelectMultiple()	设置多选框
    

    模板HTML文件

    注:模板中使用方法都是不加括号的。

    {{ form_obj.as_p }}    __>   生产一个个P标签  input  label,实际使用不使用这个
    {{ form_obj.errors }}    ——》   form表单中所有字段的错误
    {{ form_obj.username }}     ——》 一个字段对应的input框,实际使用中
    {{ form_obj.username.label }}    ——》  该字段的中文提示
    {{ form_obj.username.id_for_label }}    ——》 该字段input框的id
    {{ form_obj.username.errors }}   ——》 该字段的所有的错误
    {{ form_obj.username.errors.0 }}   ——》 该字段的第一个错误的错误
    {{ form_obj.non_field_errors.0 }} ---》 所有字段以外的错误
    

    使用form组件实现注册功能

    • views.py

      先定义一个RegForm类:

      from django import forms
      
      class RegForm(forms.Form)
      	username = forms.CharField(label='用户名',min_length=6)
          pwd = forms.CharField(label='密码',widget=forms.PasswordInput)	# widget设置密文
          hobby = forms.MultipleChoiceField(choices=((1,'篮球'),(2,'足球'),(3,'双色球')))
          ## 使用get_字段名_display()方法,获取到choices后面显示的结果。
          
          error_messages={
                  'required': '用户名是必填项',
                  'min_length': '用户名长度不能小于6位'
              }   # form组件默认错误提示都是英文,可以通过这种方式自定义成中文。
          
      # min_length:	设置最小长度
      # widget=forms.PasswordInput	设置成密文
      # choice=((),()) 
      
    • 再写一个视图函数

      # 使用form组件实现注册方式
        def register(request):
            form_obj = RegForm()
            if request.method == 'POST':
                form_obj = RegForm(data=request.POST)  
                if form_obj.is_valid():	# 对数据进行校验,返回值是True或False
                    # 校验成功  把数据插入数据中
                    # models.UserProfile.objects.create(**form_obj.cleaned_data)
                    return HttpResponse('注册成功')
        	return render(request,'register.html',{'form_obj':form_obj})
      
    • html文件

      <form action="" method="post" novalidate>
          {% csrf_token %}
          <p>
          	<label for="{{ form_obj.username.id_for_label }}">{{ form_obj.username.label }}</label>
              {{ form_obj.username }}
              {{ form_obj.username.errors.0 }}
      	</p>
      </form>
      
    • 如果部分数据需要从数据库中获取,那么数据需要随时更新,但是py文件并不是每次都加载,只加载一次放到内存中,py文件从上到下加载,RegForm()这个类中的内容只加载一次存到内存中,视图函数使用时,只是实例化这个类,并不会重新加载。只有当程序重启时才会重新加载,那么数据库中如果更新,则无法获取到新的数据。想让数据时时更新

      # 手动定义一个__init__,只要类实例化就会执行一次__init__,时时查询
      def __init__(self,*args,**kwargs):
          super(RegForm,self).__init__(*args,**kwargs)	# 继承父类,执行父类方法,否则会丢失功能
          self.fields['hobby'].choices = models.Hobby.objects.values_list('id','name')
          # values_list 由id,name组成的一个个元组的列表
          
      # self.fields会拿到RegForm类中所有字段的对象,是一个有序字典,如下
      OrderedDict([
          ('username', <django.forms.fields.CharField object at 0x0000000004108C18>), 
        ('pwd', <django.forms.fields.CharField object at 0x0000000004108F60>), 
          ('hobby', <django.forms.fields.CharField object at 0x0000000004108978>)
      ])
      
      # 直接以下面方法写
      hobby = forms.ModelMultipleChoiceField(			# 多选框
              queryset=models.Hobby.objects.all(),
          )
      

    form验证

    内置校验

    • required=True
    • min_length
    • max_length

    自定义校验器

    class RegForm(forms.Form):  # 需要一个一个字段写
        username = forms.CharField(
            label='用户名',
            min_length=6,
            initial='张三',
            required=True,
            validators=[checkname],	# 列表中填写自定义的校验器,会把字段的值自动传给校验器进行校验
            widget = forms.TextInput(attrs={...})
            # attrs定义标签属性
            
            error_messages={
                'required': '用户名是必填项',
                'min_length': '用户名长度不能小于6位'
            }
        )
        gender = forms.ChoiceField(choices=[(1,'男'),(2,'女')])	# 单选
    
    # 方式一:要定义一个函数
    from django.core.exceptions import ValidationError
    def checkname(value):	# 传的值就是字段要提交的数据,对value进行校验就行了
        # 通过校验规则 不做任何操作
        # 不通过校验规则   抛出异常
        if 'alex' in value:
            raise ValidationError('不符合社会主义核心价值观')
            
    # 方式二:内置的校验器
    from django.core.validators import RegexValidator	# 导入模块,正则校验
       # validators还有一些其他格式的校验器
    
    class RegForm(forms.Form):
    	phone = forms.CharField(
    		validators=[RegexValidator(r'^1[3-9]d{9}$', '手机号格式不正确')]
        )
    # RegexValidator(),第一个参数写正则表达式,第二个参数写错误提示
    

    钩子函数

    • 什么叫钩子:

    • 其主要思想是提前在可能增加功能的地方埋好(预设)一个钩子,这个钩子并没有实际的意义,当我们需要重新修改或者增加这个地方的逻辑的时候,把扩展的类或者方法挂载到这个点即可。

    • 局部钩子

      def clean_username(self):
          # 局部钩子,对单独一个字段进行校验
          # 通过校验规则  必须返回当前字段的值
          # 不通过校验规则   抛出异常
          v = self.cleaned_data.get('username')  # 能走到局部钩子,cleaned_data中就有值了,不然无法通过内置校验器和自定义校验器
          if 'alex' in v:
              raise ValidationError('不符合社会主义核心价值观')
          else:
              return v
      
    • 全局钩子

      def clean(self):
          # 全局钩子,对多个字段进行校验
          # 通过校验规则  必须返回当前所有字段的值
          # 不通过校验规则   抛出异常   '__all__'
          # 如果重写clean方法,必须写_validate_unique = True,不然不会校验字段的唯一性
          # 会检查model中的unique的限制
          self._validate_unique = True
          pwd = self.cleaned_data.get('pwd')
          re_pwd = self.cleaned_data.get('re_pwd')
      
          if pwd == re_pwd:
              return self.cleaned_data
          else:
              self.add_error('re_pwd','两次密码不一致!!!!!')  # 将错误信息添加到错误字典
              raise ValidationError('两次密码不一致')
      

    is_valid的校验流程

    #### is_valid的流程:执行is_valid()的流程
    1.先执行full_clean()的方法:
    	- 定义错误字典
    	- 定义存放清洗数据的字典
    2.执行_clean_fields方法:
    	- 循环所有的字段
    	- 获取当前的值
    	- 进行校验 ( 内置校验规则   自定义校验器)
    		1. 通过校验
       			self.cleaned_data[name] = value 
    			- 如果有局部钩子,执行进行校验:
    				- 通过校验——》 self.cleaned_data[name] = value 
    				- 不通过校验——》     
                    	self._errors  添加当前字段的错误 并且 self.cleaned_data中当前字段的值删除掉
    		2. 没有通过校验
    			self._errors  添加当前字段的错误
    3.执行全局钩子clean方法
    
    # 最终的判断就是错误字典中是否有值
    

    ModelForm

    • 使用方法

      ########form组件使用步骤############
      1.先导入forms模块
      2.写类,继承forms.ModelForm
      3.在类中再写元类Meta
      4.在Meta中写字段model,代表根据哪个Model生字段,---》model = models.UserProfile   
      												# UserProfile是创建表的类名
      5.字段fields,表示生成此Model中的哪些字段。__all__表示所有字段都生成,fields = '__all__';
        单独生成某几个字段
      6.如果表中没有的字段,可在类下创建
      
      
      ###########代码如下################
      
      from django import forms
      from app名称 import models
      from django.core.exceptions import ValidationError
      
      class RegForm(forms.ModelForm)	
      # 与继承forms.Form的区别在于,forms.Form要在类中写每一个需要创建的input框
      # forms.ModelForm不需要每个都写,会根据某一个Model具体的生成每一个字段
      
      	password = forms.CharField(widget=forms.PasswordIput('placeholder':'您的密码'))	# 重写样式
      	re_password = forms.CharField(widget=forms.PasswordIput('placeholder':'再次输入密码'))
          
          def __init__(self,*args,**kwargs):
              super().__init__(*args,**kwargs)
              # 限制当前新增客户为当前访问客户
              self.fields['customer'].choices = [(self.instance.customer.pk,self.instance.customer)]
              
          # 表中没有的字段可在Meta类下创建
      	class Meta:
              model = models.UserProfile	# 根据哪个Model生成字段,UserProfile是创建表的类名
              fields = '__all__'	# 生成表中的哪些字段,'__all__'是生成所有的
              # 填写指定字段['username','password']
              
              exclud = ['is_active']	# 排除is_active字段
              widgets = {		# 字典内写上指定字段的名字
                  'username':forms.TextInput(attrs={'placeholder':'您的用户名','autocomplete':'off'})
              }
              # autocomplete表示填写记录
              # 可以对表中的所有字段样式,进行修改
              # attrs中写想要更改的相关属性
              
              error.messages = {		# 错误信息
                  'username':{
                      'required':'必填'	   # 必填
                      'invalid':'请输入正确邮箱地址'	# 格式错误
                  }
              }
              # 在settings配置文件中将LANGUAGE_CODE = 'zh-Hans'
          def clean(self):
              password = self.cleaned_data.get('password',"") 
              # 当都不填时会返回None在加密时报错,所以让他获取不到password时获取一个空字符串
      
              re_password = self.cleaned_data.get('re_password',"")
              if password == re_password:
                  # 对密码进行加密
                  md5 = hashlib.md5()
                  md5.update(password.encode('utf-8'))
                  self.cleaned_data['password'] = md5.hexdigest()
                  return self.cleaned_data
              else:
                  self.add_error('re_password', '两次密码不一致')
                  raise ValidationError('两次密码不一致!!')
      
      ######视图函数######       
      def reg(request):	# 视图函数
          form_obj = RegForm()
          if request.method == 'POST':
              form_obj = RegForm(request.POST)
              if form_obj.is_valid():
                  form_obj.save()
              	return redirect(reverse('login'))
          return render(request,'reg.html',{'form_obj':form_obj})
      
      #######模板语法#######
      
      <form action='' ,method='post' novalidate>
      # novalidate 不在前端进行校验,前端写的校验会失效
          {% for field in form_obj %}
              <div class="form-group {% if field.errors %}has-error{% endif %}">
                  <label {% if not field.field.required %} style="color: #777777" {% endif %} for="{{ field.id_for_label }}" class="col-sm-2 control-label">{{ field.label }}</label>
                  <div class="col-sm-8">
                      {{ field }}
                      <span class="help-block">{{ field.errors.0 }}</span>
                  </div>
              </div>
          {% endfor %}
          
        	{{ form_obj.errors }}    ——》   form表单中所有字段的错误
          {{ form_obj.username }}     ——》 一个字段对应的input框,一般使用这个一个个input创建
          {{ form_obj.username.label }}    ——》  该字段的中文提示
          {{ form_obj.username.id_for_label }}    ——》 该字段input框的id
          {{ form_obj.username.errors }}   ——》 该字段的所有的错误
          {{ form_obj.username.errors.0 }}   ——》 该字段的第一个错误的错误
          {{ form_obj.non_filed_errors }}     ——》   # __all__的错误,不限于表单中
      </form>
      
    • 更改input框的中文名显示方式

      1.创建表时填写verbose_name属性,值为想要显示的中文名
      2.在常见input框时有label标签,可以显示中文
      

    modelformset_factory

    • modelformset_factory会先创建一个类,参数有model,Form类,extra

      • model:模型,表的类名
      • Form:form表单的类名
      • extra:默认为1,未添加数据是显示一条空数据,设置为0则可不设置该条空数据
      ModelFormSet = modelformset_factory(models.StudyRecord, StudyRecordForm, extra=0)
      
      def study_record_list(request, course_record_id):
          ModelFormSet = modelformset_factory(
              models.StudyRecord, StudyRecordForm, extra=0)
          form_set_obj = ModelFormSet(queryset=models.StudyRecord.objects.filter
                                      (course_record_id=course_record_id))
          if request.method == 'POST':
              form_set_obj = ModelFormSet(queryset=models.StudyRecord.objects.filter
                                          (course_record_id=course_record_id),data=request.POST)
              if form_set_obj.is_valid():
                  form_set_obj.save()
                  
                  return HttpResponse('保存成功')
      
          return render(request, 'teacher/study_record_list.html', {'form_set_obj': form_set_obj})
      
    • 模板中

      <!--文本不可修改的内容用该方式取-->
      <td>{{ form.instance.student }}</td>
      // form.instance 是一个对象
      // form.instance.student  通过对象拿到其中的字段值
      
      <!--在模板中使用-->
      在form标签下必须添加如下内容,固定写法
      {{ form_set_obj.management_form }}  <!--会生成4个隐藏标签-->
      <input type="hidden" name="form-TOTAL_FORMS" value="2" id="id_form-TOTAL_FORMS">
      <input type="hidden" name="form-INITIAL_FORMS" value="2" id="id_form-INITIAL_FORMS">
      <input type="hidden" name="form-MIN_NUM_FORMS" value="0" id="id_form-MIN_NUM_FORMS">
      <input type="hidden" name="form-MAX_NUM_FORMS" value="1000" id="id_form-MAX_NUM_FORMS">
      
      在循环创建form表单时,再循环下加{{ form.id }},用来标识form表单,目的找对应的对象,生成隐藏input标签
      <input type="hidden" name="form-1-id" value="2" id="id_form-1-id">
      
      <!--代码示例-->
      <form action="" method="post">
          {% csrf_token %}
          {{ form_set_obj.management_form }}
          <table class="table table-bordered table-hover">
              <thead>
              <tr>
                  <th>序号</th>
                  <th>学生</th>
                  <th>考勤</th>
                  <th>成绩</th>
                  <th>作业批语</th>
              </tr>
              </thead>
              <tbody>
              {% for form in form_set_obj %}
                  <tr>
                      {{ form.id }}
                      <td>{{ forloop.counter }}</td>
                      <td>{{ form.instance.student }}</td>
                      <td>{{ form.attendance }}</td>
                      <td>{{ form.score }}</td>
                      <td>{{ form.homework_note }}</td>
                      <td class="hidden">{{ form.student }}</td>
                      <td class="hidden">{{ form.course_record }}</td>
                  </tr>
              {% endfor %}
              </tbody>
          </table>
          <button class="btn btn-primary">保存</button>
      </form>
      
  • 相关阅读:
    网络攻击与防御 实验1
    java继承 、方法重写、重写toString方法
    递归算法
    加标签的continue用法
    程序员,你应该知道的数据结构之栈
    SpringBoot 内部方法调用,事务不起作用的原因及解决办法
    程序员,你应该知道的数据结构之哈希表
    程序员,你应该知道的数据结构之跳表
    程序员,你应该知道的二分查找算法
    程序员,你应该知道的基础排序算法
  • 原文地址:https://www.cnblogs.com/liuweida/p/12303066.html
Copyright © 2011-2022 走看看