一、Form组件
所有框架一般都有自己的表单验证组件,没有也会使用第三方的组件进行验证。组件一般都有一下功能:
- 表单验证
- 自动生成HTML标签
- 数据初始化(新建按钮、编辑按钮)
- 报错后,保留原有数据,不让用户全部重新输入。
1.1.生成HTML标签
class RegisterForm(forms.Form):
name = forms.CharField(
# required=True 代表必填项,不填则报错
required = True,
# initial 代表初始化值
initial = 'SZR',
# widget 则是使用表单生成的HTML标签
widget = forms.TextInput
)
def register(request):
form = RegisterForm()
return render(request,'register_form.html',{'form':form})
# register_form.html
{{form.name}}
运行后,得到结果:
检查源码,默认生成了标签。
<input type="text" name="name" value="SZR" required id="id_name">
默认会生成一个id_ + 字段名
也可以自己设置id
class RegisterForm(forms.Form):
name = forms.CharField(
...
widget = forms.TextInput(attrs={'id':xx1})
)
1.2 表单验证:
触发验证的代码form.is_valid()
def register(request):
if request.method == 'GET':
form = RegisterForm()
return render(request, 'register_form.html', {'form': form})
# 请求为 post 方法
form = RegisterForm(data=request.POST)
if form.is_valid():
print(form.cleaned_data)
return HttpResponse('success')
else:
print(form.errors)
return HttpResponse('fail')
mobile = forms.CharField(
required=True,
validators=[RegexValidator(r'^\d{11}$', "手机号格式错误")],
widget=forms.TextInput
)
-
required =True
当提交为空时,后台就会报错。
ps:如果要验证这个点,在html中,需要去掉浏览器的验证
需要加入<form action="post" novalidate>
-
validators=[RegexValidator(r’^\d{11}$', “手机号格式错误”)]
这个是正则表达是验证,规定写法,必须是RegexValidator
需要导入模块from django.core.validators import RegexValidator
-
钩子方法
如果是只用正则去校验,可能很多业务场景无法校验,例如,注册手机号不能重复,这样则无法校验,可以使用提供的钩子方法。固定写法
clean_ + 定义的字段名
如:
class RegisterForm(forms.Form):
mobile = ....
def clean_mobile(self):
# 查询数据库,手机号是否存在
....
raise ValidationError('手机号已存在')
定义了才会执行,没定义就不执行
ps:系统自带错误全是英文,如果需要显示为中文,则可以更改settings配置
# LANGUAGE_CODE = 'en-us'
LANGUAGE_CODE = 'zh-hans'
1.3 数据初始化
- 在定义字段的时候初始化
initial
例如
class RegisterForm(forms.Form):
name = forms.CharField(
required = True,
initial = 'SZR',
widget = forms.TextInput
)
- 在实例化form的时候初始化
form = RegisterForm(initial={"name": "SZR", "moblie": "123"})
1.2错误信息展示
发生错误,将页面重新展示,错误信息会被展示,由于错误信息是一个列表,一般展示第0个就行
1.3 循环展示所有字段
当定义的字段较多,一个个去写,就不大方便。此时就可以使用循环展示所有对象。
{% for filed in form %}
<p>{{ filed.label }}{{ filed }}{{filed.errors.0}}</p>
{% endfor %}
label则是在定义字段时,取的名字,可以展示在输入框前方
1.4关于样式
手动添加样式
widget添加attrs
class RegisterForm(forms.Form):
v1 = forms.CharField(
label="手机号",
required=True,
# initial="12345678909",
validators=[RegexValidator(r'^1[357]\d{7}$', "手机号格式错误"), ],
widget=forms.TextInput(attrs={"class":"form-control"}) # <input type="text" class="form-control"/>
)
2.自动操作
找到每个字段中的widget插件,再找到插件中的attrs属性,给每个attrs赋值**{“class”:“form-control”}**
class RegisterForm(forms.Form):
v1 = forms.CharField(...,widget=forms.TextInput)
v2 = forms.CharField(...,widget=forms.TextInput(attrs={"v1":"123"}))
def __init__(self,*args,**kwargs):
super().__init__(self,*args,**kwargs)
for name,field in self.fields.items():
if name == "v1":
continue
if field.widget.attrs:
field.widget.attrs.update({"class":"form-control"})
else:
field.widget.attrs = {"class":"form-control"}
3.写一个通用父类,再继承该父类,可以实现复用
class BootStrapForm(forms.Form):
def __init__(self, *args, **kwargs):
# 不是找父类
# 根据类的mro(继承关系),去找上个类
# super().__init__(*args, **kwargs)
for name, field in self.fields.items():
field.widget.attrs = {"class": "form-control"}
class LoginForm(BootStrapForm):
user = forms.CharField(label="用户名", widget=forms.TextInput)
pwd = forms.CharField(label="密码", widget=forms.TextInput)
def login(request):
form = LoginForm()
return render(request, "login.html", {"form": form})
二、ModelForm
ModelForm需要结合models.py
创建的表来使用
2.1 使用Form组件的操作
-
创建Form类 + 定义字段
class LoginForm(forms.Form): user = forms.CharField(label="用户名", widget=forms.TextInput) pwd = forms.CharField(label="密码", widget=forms.TextInput)
-
视图
def login(request): if request.method == "GET": form = LoginForm() return render(request, "login.html", {"form": form}) form = LoginForm(data=request.POST) if not form.is_valid(): # 校验失败 return render(request, "login.html", {"form": form}) print(form.cleaned_data) # ... return HttpRespon("OK")
-
前端
<form> {% for field in form %} <p>{{ field.label }} {{ field }} {{ field.errors.0 }}</p> {% endfor %} </form>
2.2 使用ModelForm
-
models.py
class UserInfo(models.Model): name = models.CharField(verbose_name="用户名", max_length=32) age = models.IntegerField(verbose_name="年龄") email = models.CharField(verbose_name="邮箱", max_length=128)
-
创建ModelForm
可以增加想要的字段,也可以选择需要展示的字段class LoginForm(forms.ModelForm): mobile = forms.CharFiled(label="手机号") class Meta: model = models.UserInfo fileds = ["name","age", "mobile"]
-
视图使用
def login(request): form = LoginModelForm() return render(request, "login.html", {"form": form})
-
页面
<form> {% for field in form %} <p>{{ field.label }} {{ field }} {{ field.errors.0 }}</p> {% endfor %} </form>
2.3 ModelForm的便捷之处
2.3.1 初始化数据
- 使用Form初始化数据,需要挨个挨个去赋值
class LoginForm(BootStrapForm, forms.Form):
user = forms.CharField(label="用户名", widget=forms.TextInput)
pwd = forms.CharField(label="密码", widget=forms.TextInput)
def login(request):
form = LoginForm(initial={"user": "SZR", "pwd": "123"})
return render(request, "login.html", {"form": form})
- 使用ModelForm
class LoginModelForm(forms.ModelForm):
mobile = forms.CharField(label="手机号", widget=forms.TextInput)
class Meta:
model = models.UserInfo
fields = ["name", "age", "mobile"]
widgets = {
"age": forms.TextInput,
}
labels = {
"age": "x2",
}
# 定义钩子方法
def clean_name(self):
value = self.cleaned_data['name']
# raise ValidationError("....")
return value
def login(request):
user_object = models.UserInfo.objects.filter(id=1).first()
form = LoginModelForm(instance=user_object, initial={"mobile": "12345678909"})
return render(request, "login.html", {"form": form})
ModelForm可以直接从数据库读取展示。所以比较方便
2.3.2 数据保存
- Form组件
def login(request):
if request.method == "GET":
form = LoginForm(initial={"user": "SZR", "pwd": "123"})
return render(request, "login.html", {"form": form})
form = LoginForm(data=request.POST)
if not form.is_valid():
return render(request, "login.html", {"form": form})
# form.cleaned_data
# 手动读取字典,保存至数据库
models.UserInfo.objects.create(name=form.cleaned_data['name'], pwd=form.cleaned_data['pwd'])
return HttpResponse("成功")
- ModelForm组件
form.save() # 自动将数据新增到数据库
def login(request):
if request.method == "GET":
form = LoginForm()
return render(request, "login.html", {"form": form})
form = LoginForm(data=request.POST)
if not form.is_valid():
return render(request, "login.html", {"form": form})
form.save() # 自动将数据新增到数据库
return HttpResponse("成功")
2.3.3 更新数据
- Form组件
def login(request):
if request.method == "GET":
form = LoginForm(initial={"user": "SZR", "pwd": "123"})
return render(request, "login.html", {"form": form})
form = LoginForm(data=request.POST)
if not form.is_valid():
return render(request, "login.html", {"form": form})
# 手动读取字典,跟新数据至数据库
models.UserInfo.objects.filter(id=1).update(name=form.cleaned_data['name'], pwd=form.cleaned_data['pwd'])
return HttpResponse("成功")
- ModelForm组件
def login(request):
if request.method == "GET":
form = LoginModelForm()
return render(request, "login.html", {"form": form})
user_object = model.UserInfo.object.filter(id=1).first()
form = LoginModelForm(data=request.POST, instance=user_object)
if not form.is_valid():
return render(request, "login.html", {"form": form})
form.save() # 更新id=1
return HttpResponse("成功")
总结
-
后续进行增删改查是基于数据库Models中的某个表,推荐使用:ModelForm;
-
如果要进行表单校验是与数据库的表无关直接使用Form。