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>
    
posted @ 2020-02-13 12:12  Lowell  阅读(442)  评论(0编辑  收藏  举报