1、Django中的Form表单介绍
咱们以前在HTML页面中利用form表单向后端提交数据时,都会写一些获取用户输入的标签而且用form标签把它们包起来。
与此同时咱们在好多场景下都须要对用户的输入作校验,好比校验用户是否输入,输入的长度和格式等正不正确。若是用户输入的内容有错误就须要在页面上相应的位置显示对应的错误信息.。
Django form组件就实现了上面所述的功能:
生成页面可用的HTML标签
对用户提交的数据进行校验
保留上次输入内容
2、普通方式的form表单注册
1、views.py

def register(request): error_msg = "" if request.method == "POST": username = request.POST.get("username") pwd = request.POST.get("pwd") # 对注册信息作校验 if len(username) < 6: # 用户长度小于6位 error_msg = "用户名长度不能小于6位" else: # 将用户名和密码存到数据库 UserInfo.objects.create(username='username', password='pwd') return redirect("/login/") return render(request, "register.html", {"error_msg": error_msg})
2、regirest.html

<!DOCTYPE html> <html lang="zh-CN"> <head> <meta http-equiv="content-type" charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>注册页面</title> </head> <body> <form action="/register/" method="post"> {% csrf_token %} <p> <label for="t1"></label>用户名 <input type="text" name="username" id="t1"> </p> <p> <label for="p1"></label>密码 <input type="password" name="pwd" id="p1"> </p> <p> <input type="submit" value="注册"> <p style="color: red">{{ error_msg }}</p> </p> </form> </body> </html>
3、使用form组件实现注册功能
数据库 class UserInfo(models.Model): username = models.CharField(max_length=12) password = models.CharField(max_length=20) 1、views.py 1.先定义好一个RegForm类 from django import forms # 按照Django form组件的要求本身写一个类 class RegForm(forms.Form): name = forms.CharField(max_length=12, label="用户名") pwd = forms.CharField(min_length=6, max_length=18, label="密码") 2.再写一个视图函数

# 使用form组件实现注册方式 def register(request): form_obj = RegForm() if request.method == "POST": # 实例化form对象的时候,把post提交过来的数据直接传进去 form_obj = RegForm(request.POST) # 调用form_obj校验数据的方法is_valid if form_obj.is_valid(): username = form_obj.cleaned_data.get('name') # cleaned_data会自动把form提交的数据提取出来造成一个字典 pwd = form_obj.cleaned_data.get('pwd') UserInfo.objects.create(username=username, password=pwd) return redirect("/login/") else: # 若是数据有问题 return render(request, "register.html", {'form_obj': form_obj}) return render(request, "register.html", {"form_obj": form_obj})
2、regirest.html

<!DOCTYPE html> <html lang="zh-CN"> <head> <meta http-equiv="content-type" charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>注册页面</title> <body> <form action="/register/" method="post" novalidate autocomplete="off"> {% csrf_token %} <div> <label for="{{ form_obj.name.id_for_label }}">{{ form_obj.name.label }}</label> {{ form_obj.name }} <span>{{ form_obj.name.errors.0 }}</span> </div> <div> <label for="{{ form_obj.pwd.id_for_label }}">{{ form_obj.pwd.label }}</label> {{ form_obj.pwd }} <span>{{ form_obj.pwd.errors.0 }}</span> </div> <div> <input type="submit" class="btn btn-success" value="注册"> </div> </form> </body> </html>
3、分析 form的功能: 前端页面是使用Django模板语言和form类的对象生成的 -->生成HTML标签功能 当用户名和密码输入为空或输错form组件会默认为咱们设置错误信息 -->用户提交校验功能 当用户输错以后仍保留着上次输入的内容在input框 -->保留上次输入内容 简析后端: form_obj = RegForm(request.POST) 把POST提交的数据存到RegForm进行实例化 form_obj.is_valid() 自动帮咱们去校验form_obj接收到的数据 form_obj.cleaned_data 若是校验经过,会把信息存到cleaned_data,在后端cleaned_data实际上是一个字典 取值:form_obj.cleaned_data['name']或者form_obj.cleaned_data.get('name') form_obj.errors 若是校验不经过,会把错误信息存到errors,在后端errors实际上是一个字典 取值:form_obj.errors['name']或者form_obj.errors.get('name') 简析_HTML: name字段 {{ form_obj.name }} 自动生成一个input框,属性就是咱们在views.py里面设置的form类的属性 {{ form_obj.name.id_for_label }} 关联自动生成的input框 {{ form_obj.name.label }} form对象的name的label属性的值 {{ form_obj.name.errors.0 }} 错误信息 pwd字段 {{ form_obj.pwd }} {{ form_obj.pwd.id_for_label }} {{ form_obj.pwd.label }} {{ form_obj.pwd.errors.0 }} 也可使用循环 {% for field in form_obj %} {{ field }} {{ field.id_for_label }} {{ field.label }} {{ field.errors.0 }} {% endfor %}
4、Form经常使用字段与插件
建立Form类时,主要涉及到 【字段】 和 【插件】,字段用于对用户请求数据的验证,插件用于自动生成HTML; 0、require 设置这个字段必需要填,默认也是True必填的 class LoginForm(forms.Form): username = forms.CharField( min_length=8, label="用户名", required=False # 设置成不是必需要填 ) pwd = forms.CharField(min_length=6, label="密码") 1、initial 设置input框里面的初始值。 class LoginForm(forms.Form): username = forms.CharField( min_length=8, label="用户名", initial="张三" # 设置默认值 ) pwd = forms.CharField(min_length=6, label="密码") 2、error_messages 1.局部重写错误信息,哪一个字段须要重写错误信息就在哪一个字段设置 class LoginForm(forms.Form): username = forms.CharField( min_length=8, label="用户名", initial="张三", error_messages={ "required": "不能为空", "invalid": "格式错误", "min_length": "用户名最短8位" } ) pwd = forms.CharField(min_length=6, label="密码") 2.设置全局错误信息(把默认的错误信息由英文改为中文) 在settings.py里面设置 # LANGUAGE_CODE = 'en-us' LANGUAGE_CODE = 'zh-hans' # 把语言改为中文(汉字) # TIME_ZONE = 'UTC' TIME_ZONE = 'Asia/Shanghai' # 时区改为亚洲上海 3、widget 设置input框的type类型,默认类型是text,密码框的type应该设置为password 还能够设置input框的属性,好比class的样式 class LoginForm(forms.Form): ... pwd = forms.CharField( min_length=6, label="密码", # 把密码框设置为password类型,并指定class为c1和c2的样式类 # 当出现出错时,其余类型的input框默认是保留你写的内容,只有密码框不会保存 # 想要密码框错误时也保留内容,就设置render_value=True widget=forms.widgets.PasswordInput(attrs={'class': 'c1 c2'}, render_value=True) ) 注意:在选择字段中 单选字段使用forms.fields.ChoiceField 也能够写成forms.ChoiceField 多选字段使用forms.fields.MultipleChoiceField 4、radioSelect 单选框radio,值为字符串 class LoginForm(forms.Form): username = forms.CharField( min_length=8, label="用户名", initial="张三", error_messages={ "required": "不能为空", "invalid": "格式错误", "min_length": "用户名最短8位" } ) pwd = forms.CharField(min_length=6, label="密码") gender = forms.fields.ChoiceField( choices=((1, "男"), (2, "女"), (3, "保密")), label="性别", initial=3, widget=forms.widgets.RadioSelect() ) 5、单选Select class LoginForm(forms.Form): ... hobby = forms.fields.ChoiceField( choices=((1, "篮球"), (2, "足球"), (3, "双色球"), ), label="爱好", initial=3, widget=forms.widgets.Select() ) 6、多选Select class LoginForm(forms.Form): ... hobby = forms.fields.MultipleChoiceField( choices=((1, "篮球"), (2, "足球"), (3, "双色球"), ), label="爱好", initial=[1, 3], widget=forms.widgets.SelectMultiple() ) 7、单选checkbox class LoginForm(forms.Form): ... keep = forms.fields.ChoiceField( label="是否记住密码", initial="checked", widget=forms.widgets.CheckboxInput() ) 8、多选checkbox class LoginForm(forms.Form): ... hobby = forms.fields.MultipleChoiceField( choices=((1, "篮球"), (2, "足球"), (3, "双色球"),), label="爱好", initial=[1, 3], widget=forms.widgets.CheckboxSelectMultiple() )
5、choice字段注意事项
在使用选择标签时,须要注意choices的选项能够配置从数据库中获取,可是因为Form是懒加载模式的,获取的值没法实时更新, 就是说,你建立Form的时候从数据库加载到的值是什么,它就只显示那些值,即便数据库更新了,它也不会自动更新,若是须要实现实时更新, 须要重写构造方法从而实现choice实时更新。 1、方式一 重写Form类的__init__,使其每次初始化都去查一次数据库 class RegForm(forms.Form): hobby = forms.fields.MultipleChoiceField( choices=Hobby.objects.all().values_list('id', 'name'), label='爱好', initial=[1, 3], widget=forms.widgets.CheckboxSelectMultiple() ) def __init__(self): super(RegForm, self).__init__() self.fields['hobby'].choices=Hobby.objects.all().values_list('id', 'name') 2、方式二 使用From里面的models模块,这个模块是依赖于APP里面的models的(注意:两个models不是同一个),获得的是一个个对象, 全部还要要操做APP里面的models,使其显示出具体的值 APP下的models.py class Hobby(models.Model): name = models.CharField(max_length=12) def __str__(self): return self.name views.py from django.forms import models as f_models class RegForm(forms.Form): hobby2 = f_models.ModelMultipleChoiceField( queryset=Hobby.objects.all(), label='第二爱好', initial=[1, 2], widget=forms.widgets.CheckboxSelectMultiple() )
6、Django Form全部内置字段

Field required=True, 是否容许为空 widget=None, HTML插件 label=None, 用于生成Label标签或显示内容 initial=None, 初始值 help_text='', 帮助信息(在标签旁边显示) error_messages=None, 错误信息 {'required': '不能为空', 'invalid': '格式错误'} validators=[], 自定义验证规则 localize=False, 是否支持本地化 disabled=False, 是否能够编辑 label_suffix=None Label内容后缀 CharField(Field) max_length=None, 最大长度 min_length=None, 最小长度 strip=True 是否移除用户输入空白 IntegerField(Field) max_value=None, 最大值 min_value=None, 最小值 FloatField(IntegerField) ... DecimalField(IntegerField) max_value=None, 最大值 min_value=None, 最小值 max_digits=None, 总长度 decimal_places=None, 小数位长度 BaseTemporalField(Field) input_formats=None 时间格式化 DateField(BaseTemporalField) 格式:2015-09-01 TimeField(BaseTemporalField) 格式:11:12 DateTimeField(BaseTemporalField)格式:2015-09-01 11:12 DurationField(Field) 时间间隔:%d %H:%M:%S.%f ... RegexField(CharField) regex, 自定制正则表达式 max_length=None, 最大长度 min_length=None, 最小长度 error_message=None, 忽略,错误信息使用 error_messages={'invalid': '...'} EmailField(CharField) ... FileField(Field) allow_empty_file=False 是否容许空文件 ImageField(FileField) ... 注:须要PIL模块,pip3 install Pillow 以上两个字典使用时,须要注意两点: - form表单中 enctype="multipart/form-data" - view函数中 obj = MyForm(request.POST, request.FILES) URLField(Field) ... BooleanField(Field) ... NullBooleanField(BooleanField) ... ChoiceField(Field) ... choices=(), 选项,如:choices = ((0,'上海'),(1,'北京'),) required=True, 是否必填 widget=None, 插件,默认select插件 label=None, Label内容 initial=None, 初始值 help_text='', 帮助提示 ModelChoiceField(ChoiceField) ... django.forms.models.ModelChoiceField queryset, # 查询数据库中的数据 empty_label="---------", # 默认空显示内容 to_field_name=None, # HTML中value的值对应的字段 limit_choices_to=None # ModelForm中对queryset二次筛选 ModelMultipleChoiceField(ModelChoiceField) ... django.forms.models.ModelMultipleChoiceField TypedChoiceField(ChoiceField) coerce = lambda val: val 对选中的值进行一次转换 empty_value= '' 空值的默认值 MultipleChoiceField(ChoiceField) ... TypedMultipleChoiceField(MultipleChoiceField) coerce = lambda val: val 对选中的每个值进行一次转换 empty_value= '' 空值的默认值 ComboField(Field) fields=() 使用多个验证,以下:即验证最大长度20,又验证邮箱格式 fields.ComboField(fields=[fields.CharField(max_length=20), fields.EmailField(),]) MultiValueField(Field) PS: 抽象类,子类中能够实现聚合多个字典去匹配一个值,要配合MultiWidget使用 SplitDateTimeField(MultiValueField) input_date_formats=None, 格式列表:['%Y--%m--%d', '%m%d/%Y', '%m/%d/%y'] input_time_formats=None 格式列表:['%H:%M:%S', '%H:%M:%S.%f', '%H:%M'] FilePathField(ChoiceField) 文件选项,目录下文件显示在页面中 path, 文件夹路径 match=None, 正则匹配 recursive=False, 递归下面的文件夹 allow_files=True, 容许文件 allow_folders=False, 容许文件夹 required=True, widget=None, label=None, initial=None, help_text='' GenericIPAddressField protocol='both', both,ipv4,ipv6支持的IP格式 unpack_ipv4=False 解析ipv4地址,若是是::ffff:192.0.2.1时候,可解析为192.0.2.1, PS:protocol必须为both才能启用 SlugField(CharField) 数字,字母,下划线,减号(连字符) ... UUIDField(CharField) uuid类型
7、Form校验规则
1、使用RegexValidator验证器(正则表达式) from django.core.validators import RegexValidator # 导入使用正则表达的模块 class RegForm(forms.Form): phone = forms.CharField( label='手机号', widget=forms.widgets.TextInput(attrs={'class': 'form-control'}), validators=[RegexValidator(r'^1[3-9]\d{9}$', '手机号码不正确')] ) validators=一个可迭代对象 可迭代对象里面调用RegexValidator方法 RegexValidator方法有两个参数,一个是用于校验的正则表达式,另外一个是匹配失败时的错误提示 2、自定义校验规则 from django.core.exceptions import ValidationError # 专门屏蔽'JPM'的校验规则 def check_jpm(value): # value就是用户输入的值 if 'JPM' in value: raise ValidationError('包含敏感词汇') class RegForm(forms.Form): name = forms.CharField( min_length=2, max_length=12, label="用户名", widget=forms.widgets.TextInput(), validators=[check_jpm] # 直接调用自定义的函数 ) 3、使用RegexField字段 class RegForm(forms.Form): phone = forms.RegexField( label='手机号码', regex=r'^1[3-9]\d{9}$', widget=forms.widgets.TextInput(attrs={'class': 'form-control'}) )
8、钩子(Hook)函数
1、介绍 局部钩子函数: 在Form类里面实现以clean_开头,字段结尾的函数,例如clean_name, 在实例化对象调用is_valid()方法进行校验的时候,会自动执行clean_方法。 局部钩子函数:在实例化对象的时候,数据已经存在cleaned_data里面了 可是局部钩子函数是循环每个具体的字段(例如:name, pwd等),因此在局部钩子函数只能取到它自己的值 若是抛出错误,则把错误信息添加进errors字典里面 若是没有抛出错误,则把这个字段对应的cleaned_data的值修改为return返回的值(通常直接返回本来的值,不作修改) 全局钩子函数: 在Form类里面实现以clean函数, 在实例化对象调用is_valid()方法进行校验的时候,会自动执行clean方法。 全局钩子:能够取到cleaned_data全部的数据 若是没有抛出错误,把返回值直接赋值给cleaned_data这个变量 若是抛出错误,须要手动调用add_error()方法,明确指定给哪一个字段增长错误信息 注意:局部钩子函数没有抛出错误是修改cleaned_data字典里面的某个值 而全局钩子没有抛出错误是直接修改cleaned_data这个变量,能够把cleaned_data改为列表、数字等 通常没有抛出错误就直接把原来的cleaned_data字典从新赋值给cleaned_data变量 2、示例

from django.core.validators import RegexValidator from django.core.exceptions import ValidationError class RegForm(forms.Form): name = forms.CharField( min_length=2, max_length=12, label="用户名", # 利用error_messages自定义错误信息 error_messages={'min_length': '用户名至少2位', 'max_length': '用户名最多12位'}, widget=forms.widgets.TextInput(attrs={'class': 'form-control'}), ) pwd = forms.CharField( min_length=6, max_length=18, label="密码", # 把密码框设置为password类型,并指定class为form-control样式类,render_value=True设置 密码错误时,把密码回写到输入框里(即密码错误时也保存密码) widget=forms.widgets.PasswordInput(attrs={'class': 'form-control'}, render_value=True) ) re_pwd = forms.CharField( min_length=6, max_length=18, label="确认密码", required=False, # 设置成不是必需要填的字段 widget=forms.widgets.PasswordInput(attrs={'class': 'form-control'}) ) phone = forms.CharField( label='手机号', widget=forms.widgets.TextInput(attrs={'class': 'form-control'}), validators=[RegexValidator(r'^1[3-9]\d{9}$', '手机号码不正确')] ) # 局部钩子函数:在实例化对象的时候,数据已经存在cleaned_data里面了 # 可是局部钩子函数是循环每个具体的字段(例如:name, pwd等),因此在局部钩子函数只能取到它自己的值 # 若是抛出错误,则把错误信息添加进errors字典里面 # 若是没有抛出错误,则把这个字段对应的cleaned_data的值修改为return返回的值(通常直接返回本来的值,不作修改) def clean_name(self): # 局部钩子只能cleaned_data.get('本身的值') # cleaned_data.get('phone') 取不到 value = self.cleaned_data.get('name') if 'JPM' in value: raise ValidationError('太sq了,不能填') else: return value # 全局钩子:能够取到cleaned_data全部的数据 # 若是没有抛出错误,把返回值直接赋值给cleaned_data这个变量 # 若是抛出错误,须要手动调用add_error()方法,明确指定给哪一个字段增长错误信息 # 注意:局部钩子函数没有抛出错误是修改cleaned_data字典里面的某个值 # 而全局钩子没有抛出错误是直接修改cleaned_data这个变量,能够把cleaned_data改为列表、数字等 # 通常没有抛出错误就直接把原来的cleaned_data字典从新赋值给cleaned_data变量 def clean(self): 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('两次密码不一致')
9、在Form组件应用Bootstrap样式
1、可经过重写form类的init方法来实现。 class LoginForm(forms.Form): username = forms.CharField( min_length=8, label="用户名", initial="张三", error_messages={ "required": "不能为空", "invalid": "格式错误", "min_length": "用户名最短8位" } # 重写方式一: def __init__(self, *args, **kwargs): super(LoginForm, self).__init__(*args, **kwargs) for field in iter(self.fields): self.fields[field].widget.attrs.update({ 'class': 'form-control' }) # 重写方式二: def __init__(self, *args, **kwargs): super(LoginForm, self).__init__(*args, **kwargs) # self.fields是一个大字典,key是字段名,value是字段对象 for field in self.fields.values(): field.widget.attrs.update({'class': 'form-control'})
10、Form组件使用ajax提交示例
1、register.html

<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>注册</title> {% load static %} <link rel="stylesheet" href="{% static 'bootstrap-3.3.7/css/bootstrap.css' %}"> </head> <body> <div class="container"> <div class="row"> <div class="col-md-4 col-md-offset-4" style="margin-top: 70px"> <form novalidate id="f1"> {% csrf_token %} {% for field in form_obj %} <div class="form-group"> <label for="{{ field.id_for_label }}">{{ field.label }}</label> {{ field }} <span class="help-block"></span> </div> {% endfor %} <div> <button type="button" id="b1" class="btn btn-success">注册</button> </div> </form> </div> </div> </div> <script src="{% static 'jquery.js' %}"></script> <script> $('#b1').click(function () { // 自定义一个对象用来存储表单的提交信息 var dataObj = {}; $('input').each(function () { dataObj[$(this).attr('name')] = $(this).val(); }); console.log(dataObj); // 取到input标签的值 $.ajax({ url: '/register/', type: 'post', data: dataObj, success: function (res) { console.log(res); // 把全部的错误提示信息展现到页面的指定位置 if (res.code === 0){ // 没错 location.href = res.url; }else { $.each(res.error_msg, function (k, v) { // 根据k找到对应的input框,把v中第一个字符串显示出来 $('#id_'+k).next('span').text(v[0]).parent().addClass('has-error'); }) } } }) }); // input标签获取焦点以后清除以前的错误提示 $('form input').focus(function () { $(this).next('span').text('').parent().removeClass('has-error'); }) </script> </body> </html>
2、models.py

from django.db import models # Create your models here. GENDER_CHOICES = ((0, '男'), (1, '女'), (2, '保密')) DEFAULT_GENDER = 2 class UserInfo(models.Model): username = models.CharField(max_length=12, unique=True) password = models.CharField(max_length=20) phone = models.CharField(max_length=11, unique=True) email = models.EmailField() gender = models.PositiveIntegerField(choices=GENDER_CHOICES, default=DEFAULT_GENDER) def __str__(self): return self.username
3、Form组件单独写一个py文件 forms.py

from django import forms from django.core.validators import RegexValidator from django.core.exceptions import ValidationError from app01.models import GENDER_CHOICES, DEFAULT_GENDER, UserInfo class RegForm(forms.Form): username = forms.CharField( max_length=12, min_length=2, label='用户名', widget=forms.widgets.TextInput() ) password = forms.CharField( max_length=20, min_length=6, label='密码', widget=forms.widgets.PasswordInput() ) re_password = forms.CharField( max_length=20, min_length=6, label='确认密码', widget=forms.widgets.PasswordInput() ) phone = forms.RegexField( regex=r'^1[3-9]\d{9}$', label='手机号码', max_length=11, min_length=11, widget=forms.widgets.TextInput(), ) email = forms.CharField( label='邮箱', widget=forms.widgets.EmailInput(), validators=[RegexValidator(r'^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*\.[a-zA-Z0-9]{2,6}$', '邮箱格式不正确'), ] ) gender = forms.ChoiceField( label='性别', widget=forms.widgets.RadioSelect(), choices=GENDER_CHOICES, initial=DEFAULT_GENDER, ) # 写一个局部钩子方法,校验用户名是否已被注册 def clean_name(self): name_value = self.cleaned_data.get('name') is_exist = UserInfo.objects.filter(name=name_value) if is_exist: raise ValidationError('用户名已存在') else: return name_value # 写一个局部钩子方法,校验手机号是否已被注册 def clean_phone(self): phone_value = self.cleaned_data.get('phone') is_exist = UserInfo.objects.filter(phone=phone_value) if is_exist: raise ValidationError('手机号已存在') else: return phone_value def clean(self): # 判断密码和确认密码是否相等 # self.cleaned_data --> 全部通过校验的数据 pwd = self.cleaned_data.get('password') re_pwd = self.cleaned_data.get('re_password') if pwd == re_pwd: return self.cleaned_data else: self.add_error('re_password', '两次密码不一致') raise ValidationError('两次密码不一致') def __init__(self, *args, **kwargs): super(RegForm, self).__init__(*args, **kwargs) for field in iter(self.fields): if field == 'gender': continue self.fields[field].widget.attrs.update({ 'class': 'form-control' })
4、views.py

from django.shortcuts import render, HttpResponse, redirect from app01.forms import RegForm from app01.models import UserInfo from django.http import JsonResponse # Create your views here. # 登陆 def login(request): return HttpResponse('login') # 注册 def register(request): if request.method == 'POST': res = {'code': 0} # 利用post提交的数据实例化form类 form_obj = RegForm(request.POST) # 校验数据的有效性 if form_obj.is_valid(): # form_obj2.cleaned_data form_obj.cleaned_data.pop('re_password') UserInfo.objects.create(**form_obj.cleaned_data) res['url'] = '/login/' else: # 数据有问题 res['code'] = 1 res['error_msg'] = form_obj.errors return JsonResponse(res) form_obj = RegForm() return render(request, 'register.html', {'form_obj': form_obj}) # 首页 def index(request): return HttpResponse('index')
11、ModelForm
1、介绍 咱们在设计Form表单的时候,通常都会把字段名设置成和数据库(ORM)的字段名如出一辙,由于这样设计的话,你后面设计的逻辑中,若是须要建立用户, 那么能够从表单的数据中提取出来,用关键字参数**kwargs直接建立,若是Form表单的字段和ORM的不同,那么建立用户的时候就须要一个个指定, 好比username=name,password=pwd等。 那么问题来了,Form表单的字段和ORM的字段同样,那么写Fomr表单的时候不就等于再写一次ORM的字段吗,有简单的方法吗? 显然是有的,Django给咱们考虑到了。 用法是: 建Form表单类的时候继承ModelForm,在Meta类里面指定继承ORM的哪些字段,也能够新增你要的字段。 2、示例 from django import forms from crm.models import UserProfile # 导入UserProfile这张ORM表 class RegForm(forms.ModelForm): # 继承ModelForm # 新增的字段(UserProfile中并无这个字段的) re_password = forms.CharField( label='确认密码', widget=forms.widgets.PasswordInput() ) class Meta: model = UserProfile # 代表使用UserProfile这张表做为模型 # fields = '__all__' # 展现UserProfile的全部字段 fields = ['email', 'password', 're_password', 'name', 'mobile'] # 按照顺序展现列表中的字段 # exclude = [''] # 把不须要的字段排查,其余字段展现出来 labels = { 'email': '邮箱' } error_messages = { 'email': { 'max_length': '邮箱长度过长', 'unique': '邮箱不能为空' } } widgets = { 'password': forms.widgets.PasswordInput() } # 重写init方法批量应用bootstrap样式 def __init__(self): super().__init__() # self.fields是一个大字典,key是字段名,value是字段对象 for field in self.fields.values(): field.widget.attrs.update({'class': 'form-control'}) 三、class Meta下的配置 1. model = 'ORM类名' 2. 字段 1. fields = '__all__' # 展现ORM表的全部字段 2. fields = ['要展现的字段1', '字段2'] # 指定的字段显示出来,其余字段不显示 3. exclude = ['须要排除的字段1', ...] # 指定的字段不显示,其余字段都显示出来 3. labels = { '字段名1': 'label名1', '字段名2': 'label名2', } 4. error_messages = { '字段名': { 'min_length': '最小长度不能小于xx位', 'max_length': '最大长度不能超过xx位', 'required': '这个字段是必填项' } } 5. widgets = { 'password': forms.widgets.PasswordInput() } 6.help_texts = None # 帮助提示信息 7. 校验 1. 能够在model的字段中配置validators 2. 本身在Form类中重写字段,定义validators配置 3. 局部钩子方法和全局钩子方法 同Form的用法 4、ModelForm中含有外键字段 建立ModelForm的时候,若是model中有含有外键字段, 那么这个外键字段在ModelForm默认是单选的select字段,label默认是model的verbose_name的值,若是没有则显示字段名, 或者能够在modelform的Meta里面经过labels设置,choices默认是这个外键关联的全部数据,HTML上的select标签的value默认是主键, 显示的文本默认是这个对象。 注意:若是是多对多字段,则生成多选的select字段 例如: 1.models.py class ConsultRecord(models.Model): customer = models.ForeignKey('Customer', verbose_name="所咨询客户") 2.modelsform class ConsultRecordForm(forms.ModelForm): class Meta: model = ConsultRecord fields = '__all__' 上面的modelsform字段customer实际上等于下面这个普通的form class ConsultRecordForm(forms.Form): customer = forms.fields.ChoiceField( choices=Customer.objects.all().values_list('id', 'name'), label="所咨询客户", # verbose_name widget=forms.widgets.Select() ) 5、save()方法 每一个ModelForm还具备一个save()方法。 这个方法根据表单绑定的数据建立并保存数据库对象。 ModelForm的子类能够接受现有的模型实例做为关键字参数instance的值,若是提供instance参数,则save()将更新该实例。 若是没有提供,save()将建立模型的一个新实例。 注意:instance接收的是一个实例化对象,普通的Form并无instance参数,且当modelform中含有多对多字段时,调用save方法后, 会自动帮你更新本身的表和多对多的第三张表,不然,你得本身手动更新本身的表,而后再经过多对多字段找到第三张表再次更新。 1.没有提供参数instance,save()建立新的实例 def reg(request): if request.method == 'POST': form_obj = RegForm(request.POST) # form_obj是一个ModelForm对象,它和数据库的Model类对应 form_obj.save() # 至关于UserProfile.objects.create(**form_obj.cleaned_data) return redirect('/login/') 2.提供参数instance,save()跟新这个实例 1.models.py from django.db import models # Create your models here. class Publisher(models.Model): name = models.CharField(max_length=12) addr = models.CharField(max_length=255) 2.forms.py from django import forms from myapp.models import Publisher class PublisherForm(forms.ModelForm): class Meta: model = Publisher fields = '__all__' 3.views.py from django.shortcuts import render, redirect from myapp.models import Publisher from myapp.forms import PublisherForm # Create your views here. def publisher_list(request): data = Publisher.objects.all() return render(request, 'publisher_list.html', {'publisher_list': data}) def edit_publisher(request): edit_id = request.GET.get('id') publisher_obj = Publisher.objects.filter(id=edit_id).first() # instance=某个对象:把这个对象的原始数据填到PublisherForm这个表单里面 form_obj = PublisherForm(instance=publisher_obj) if request.method == 'POST': # 把request.POST的数据更新到instance=这个对象里面 form_obj = PublisherForm(request.POST, instance=publisher_obj) # DRF框架的时候也会用到 if form_obj.is_valid(): # 去数据库编辑 # 方法一: # new_name = form_obj.cleaned_data.get('name') # new_addr = form_obj.cleaned_data.get('addr') # publisher_obj.name = new_name # publisher_obj.addr = new_addr # publisher_obj.save() # 方法二: form_obj.save() return redirect('/publisher_list/') return render(request, 'edit_publisher.html', {'form_obj': form_obj})
12、formset_factory的使用
1.介绍 from django.forms import formset_factory 对于继承forms.Form的Form类,咱们可使用formset_factory。 注意:modelform类其实也继承了forms.Form的父类,所以modelform也可以使用formset_factory 2. 必填参数 1. form --> 继承了forms.form的form类 3. 额外参数 extra:想要生成的空表单的数量 max_num:最多能够展现的表单数量 initial: 初始化formset的默认值(实例化formset时候的参数) 注意: max_num优先级高于extra,好比,咱们想要显示5个空表单(extra=5),但max_num=2,最后只会显示2个空表单 4. 返回值 1. 是一个类 2. 示例 FormSet = formset_factory(CourseForm) # 返回的FormSet是一个类 formset_obj = FormSet(initial=[{}, {}...]) 5. 注意事项 1. 生成的FormSet在html页面中使用的时候,每个form对象必须带上本身的form.id标识这是哪一个对象的form,页面是会默认hidden这个参数(添加的时候不须要,编辑的时候须要) 2. 若是要提交这些form对象,则须要在html中的form表单为formset设置一个参数:{{ formset_obj.management_form }},页面是会默认hidden这个参数 是告诉后端我这个formset有多少个小form 3. FormSet默认会给你多生成一个form对象,能够经过参数 extra 设置额外加多少个,0表明不额外加form对象 4. FormSet其实就是批量生成Form对象的操做,注意是Form对象 6. formset_factory和form同样,没有instance参数,因此也没有queryset参数 7. 其余操做和单个form操做是同样的,只是这个formset等于批量操做而已,同样有is_valid等(没有save,由于form是没有save的,modelform才有) 1. form表单 class CourseForm(forms.Form): title = forms.CharField(max_length=32) score = forms.CharField(max_length=32) 2. views.py from django.forms import formset_factory def course(request): FormSet = formset_factory(CourseForm, extra=1) formset_obj = FormSet(initial=[{}, {}...]) if request.method == 'POST': formset_obj = FormSet(request.POST) if formset_obj.is_valid(): # 手动建立Course objs = (Course(**item) for item in formset_obj.cleaned_data) Course.objects.bulk_create(objs) return redirect(...) return render(request, 'xx.html', {'formset_obj': formset_obj})
十3、modelformset_factory的使用
1. 介绍 from django.forms import modelformset_factory 对于继承forms.ModelForm的Form类,咱们可使用modelformset_factory 2. 必填参数 1. model --> 数据库中的某个表 2. modelform --> 继承了forms.ModelForm的类 3. 其余参数 extra:想要生成的空表单的数量 max_num:最多能够展现的表单数量 queryset:初始化modelformset(实例化modelformset时候的参数) 4. 返回值 1. 是一个类 2. 示例 FormSet = modelformset_factory(StudyRecord, StudyRecordForm) # 返回的FormSet是一个类 query_set = StudyRecord.objects.filter(course_record_id=course_record_id) formset_obj = FormSet(queryset=query_set) 5. 注意事项 1. 生成的FormSet在html页面中使用的时候,每个modelform对象必须带上本身的modelform.id标识这是哪一个对象的modelform,页面是会默认hidden这个参数(添加的时候不须要,编辑的时候须要) 2. 若是要提交这些modelform对象,则须要在html中的modelform表单为formset设置一个参数:{{ formset_obj.management_form }},页面是会默认hidden这个参数 是告诉后端我这个modelformset有多少个小modelform 3. FormSet默认会给你多生成一个modelform对象,能够经过参数 extra 设置额外加多少个,0表明不额外加modelform对象 4. FormSet其实就是批量生成ModelForm对象的操做,注意是ModelForm对象 5. 在html页面仍然可使用具体的某个modelform对象的instance参数拿到传进来的对象,可是若是使用的不是本身的modelform对象的字段,提交数据时并不生效 6. modelformset_factory实例设置默认值用queryset而不是instance,queryset至关于为每一个modelform设置instance 7. 其余操做和单个modelform操做是同样的,只是这个modelformset等于批量操做而已,同样有is_valid,save等 8. 示例 1. views.py # 学习记录列表 from django.forms import modelformset_factory def study_record_list(request, course_record_id): # 生成一个formset类 FormSet = modelformset_factory(StudyRecord, StudyRecordForm, , extra=0) # extra=0默认不给我生成额外的form对象 # 拿到这节课的全部同窗的学习记录(多个StudyRecord实例组成的query_set) query_set = StudyRecord.objects.filter(course_record_id=course_record_id) # 从query_set中取出每个StudyRecord实例生成form表单,存到formset里面 formset_obj = FormSet(queryset=query_set) # formset_obj是一个可迭代对象,里面是一个个小form # 上面的代码至关于: # form_obj1 = StudyRecordForm(instance=query_set[0]) # form_obj2 = StudyRecordForm(instance=query_set[1]) ... # 而后把这些form_obj存到formset_obj里面 if request.method == 'POST': formset_obj = FormSet(request.POST, queryset=query_set) if formset_obj.is_valid(): formset_obj.save() return render(request, 'study_record.html', {'formset_obj': formset_obj}) 2. study_record.html部分代码 <form action="" method="post"> {% csrf_token %} {{ formset_obj.management_form }} <!--提交formset时必需要添加的参数--> <div class="col-md-12"> <table class="table table-striped table-bordered"> <thead> <tr> <th>#</th> <th>姓名</th> <th>考勤</th> <th>成绩</th> <th>做业</th> </tr> </thead> <tbody> {% for form_obj in formset_obj %} <tr> {{ form_obj.id }} <td>{{ forloop.counter }}</td> <td>{{ form_obj.instance.student.name }}</td> <!--经过form对象的instance参数取到对应的对象的值--> <td style="display: none">{{ form_obj.student }}</td> <!--上面的值不会提交上去,因此设置一个form对象自己的字段值隐藏起来--> <td>{{ form_obj.attendance }}</td> <!--这样就达到了,即不让别人修改我指定的字段,并且这个字段还能提交上去--> <td>{{ form_obj.score }}</td> <td>{{ form_obj.homework_note }}</td> </tr> {% endfor %} </tbody> </table> <button class="btn btn-success pull-right" type="submit">保存</button> </div> </form>