1 form总结
# Form数据格式验证
- 原理:
- 流程
a.写类LoginForm(Form):
字段名 = fields.xxFields() # 验证规则,本质是正则表达式(fields.xxFields()是一个正则表达式)
字段名 = fields.xxFields() # 验证规则,本质是正则表达式
b. obj = LoginForm(request.POST)
c. 验证数据result = obj.is_valid()
d. 拿到符合格式的数据 obj.cleaned_data
e. 不符合格式,获取错误信息 obj.errors - Form提交数据验证程序(没有实现保留上次输入的值)
# 前端
<form action="/app02/login" method="POST">
{% csrf_token %}
<p>
<input type="text" name="user" placeholder="用户名">
<span style="color:red;">{{ error.user.0 }}</span>
</p>
<p>
<input type="password" name="pwd" placeholder="密码">
<span style="color:red;">{{ error.pwd.0 }}</span>
</p>
<p><input type="submit" value="提交"></p>
</form> # LoginForm类
class LoginForm(Form):
user = fields.CharField(required=True,
error_messages={
'required': '不能为空',
})
pwd = fields.CharField(required=True,
min_length=8,
error_messages={
'required': '不能为空',
'min_length': '长度必须大于8'
}) # 视图
def login(request):
if request.method == 'GET':
return render(request, 'app02_login.html')
else:
obj = LoginForm(request.POST)
# 检验提交数据是否符合规则
if obj.is_valid():
print(obj.cleaned_data)
# obj.cleaned_data是一个字典,form表单提交的数据
#{'password': 'aaaaaaaaaa', 'username': 'alexadfdda'}
return redirect('/app02/login')
else:
return render(request, 'app02_login.html', {'error': obj.errors}) # Form保留上一次输入数据
- 原理
b.Form提交,验证数据同时保留上次输入的值
1.生成html标签操作
- widget # 选择input的类型,可为Textarea,select
只要,前端:{{ obj.t1 }}或者 {{obj.as_p}}
视图:无论get还是Post都将obj传给前端 2 利用上面的几个参数保留上次输入的内容
a.原理: 利用Form组件可以生成标签
GET:
obj = TestForm()
{{ obj.t1 }} 等效成 <input type="text" name="t1">
POST:
obj = TestForm(request.POST)
{{ obj.t1}} 等效成<input type="text" name="t1" value = "你提交的数据">
{{ obj.errors.t1.0 }} 显示错误信息 总之, 前端:{{ obj.t1 }}或者 {{obj.as_p}}
视图:无论get还是Post都将obj传给前端
- 实例 # FormL类
class TestForm(Form):
t1 = fields.CharField(
required=True,
min_length=4,
max_length=8,
widget=widgets.TextInput,
label='用户名',
label_suffix=':',
help_text='4-8个字符',
initial='root'
)
t2 = fields.CharField(
required=True,
min_length=8,
widget=widgets.PasswordInput,
label='密码',
label_suffix=':',
initial='password'
)
#视图
def test(request):
if request.method == 'GET':
obj = TestForm()
return render(request, 'app02_test.html', {'obj': obj})
else:
obj = TestForm(request.POST)
if obj.is_valid():
# 数据库添加数据
return redirect("/app02_index.html")
else:
return render(request, 'app02_test.html', {'obj': obj}) # 前端
<form action="/app02/test" method="POST" novalidate>
{% csrf_token %}
{{ obj.as_p }}
<p><input type="submit" value="提交"></p>
</form> # ajax实现验证数据 + 保留上次输入的值
- ajax提交(能验证数据 + 保留上次输入的值)
# 模板
<form action="/app02/ajax_login" method="POST" id="form1">
{% csrf_token %}
<p>
<input type="text" name="user" placeholder="用户名">
<span style="color:red;">{{ error.user.0 }}</span>
</p>
<p>
<input type="password" name="pwd" placeholder="密码">
<span style="color:red;">{{ error.pwd.0 }}</span>
</p>
<p><a onclick="submitForm();">ajax提交数据</a></p>
</form>
<script src="/static/js/jquery-1.12.4.js"></script>
<script>
function submitForm() {
$(".temp").remove();
$.ajax({
url: '/app02/ajax_login',
type: 'POST',
data: $('#form1').serialize(),
dataType: 'JSON',
success: function (arg) {
console.log(arg);
if(arg.status){ }else{
$.each(arg.msg, function (index, value) {
tag = document.createElement('span');
tag.innerHTML = value[0];
tag.style.color = 'red';
tag.className = "temp";
$('#form1').find('input[name="' + index + '"]').after(tag); }) }
}
})
}
</script> # 视图
def ajax_login(request):
if requset.method = 'GET':
return render(request, 'ajax_login.html')
else:
ret = {'status': True, 'msg': None}
obj = LoginForm(request.POST)
if obj.is_valid():
print(obj.cleaned_data)
else:
ret['status'] = False
ret['msg'] = obj.errors
import json
return HttpResponse(json.dumps(ret))
# 可以返回render, 因为render实际就是调用HttpResponse # Form生成html标签原理
a. 通过Form生成Input输入框,Form标签,以及submit标签还是要在前端写的,
但是Form标签内的Input标签可以在后台实现;只需要按以下步骤
- views定义StudentForm(Form)类
- views视图函数将Form实例化对象传递给前端
- 前端{{ obj.段 }}即可 b. 通过Form设置前端Input的type属性,即设置不同类型的输入框
# 设置name为text, cls_id为下拉框
class StudentForm(Form):
name = fields.CharField(widget= widgets.InputText())
cls_id = fields.IntegerField(widget = widgets.Select) c. 设置下拉框的内容choices属性
class StudentForm(Form):
cls_id = fields.IntegerField(
widget=widgets.Select(choices=models.Classes.objects.values_list('id', 'title'))
)
注意: choices的值必须[元组,(), ()]类型
widget=widgets.Select(choices=[(1, '上海'), (2,'北京')]) d.设置input输入框的class属性 -- attrs
name = fields.CharField(max_length=8, min_length=2,
widget=widgets.TextInput(attrs={'class': 'form-control'})
)
cls_id = fields.IntegerField(
widget=widgets.Select(
choices=models.Classes.objects.values_list('id', 'title'),
attrs={'class': 'form-control'}
)
) 注意: attrs参数必须放在TextInput或者Select等内部,而且值必须为字典 #通过Form设置前端Input的默认显示值原理
只要在视图函数将实例化一个Form对象,并且设置initial值即可,但对单选和多选有区别
- 单选
student_dict = models.Student.objects.filter(id=nid).values('name', 'age', 'email', 'cls_id').first()
obj = StudentForm(initial=student_dict)
# initial必须是字典
return render(request, "a.html", {'obj': obj} # 多对多时,如何将表单数据插入到数据库中
- 新增老师的时候,如何将提交的数据插入到数据库两张表上(老师表和第三张表)
class_list = obj.cleaned_data.pop('cls')
# 将老师任教班级数据pop出来,此时cleaned_data= {'name': xxx}
teacher = models.Teacher.objects.create(**obj.cleaned_data)
teacher.cls.add(*class_list) # 多对多时,页面如何显示第三张表的数据
- 添加老师成功后,跳转到teachers页面,如何显示老师任教班级的数据(老师和班级的关联信息是放在第三张表app01_teacher_cls上)
item.cls是一个objects对象,后面可以接values, all, values_list
{% for item in teacher_list %}
<tr>
<td>{{ item.id }}</td>
<td>{{ item.name }}</td>
<td>
{% for x in item.cls.values_list %}
{{ x.1}}
{% endfor %}
</td> {% endfor %} # BUG:页面刷新时,无法动态显示数据库内容
分别打开添加老师(或者学生)的页面,和添加班级的页面, 然后再添加班级页面新添加一个班级。
刷选添加老师(或者学生)页面,发现班级下拉框并没有动态增加刚才新增加的班级。 原因分析:出现在class TeacherForm和StudentForm定义上,以TeacherForm为例
class TeacherForm(Form):
name = fields.CharField(max_length=16,
widget=widgets.TextInput(attrs={'class': 'form-control'})
)
cls = fields.MultipleChoiceField(
choices=models.Classes.objects.values_list('id', 'title'),
widget=widgets.SelectMultiple(attrs={'class': 'form-control'})
) 在实例化一个TeacherForm对象时,由于name, cls为类变量,所以这两个类变量只要第一次生成后,
后面实例化对象时,这两个变量是不会改变的。
在调用父类init函数时,会将cls, name放到父类的self.fields里面
self.fields = {'name': name, 'cls': cls} 因此解决办法出来了,在每一次实例化对象时,再获取数据库的值给cls,
重新刷新self.fields里面的cls字段内容
class TeacherForm(Form):
name = fields.CharField(max_length=16,
widget=widgets.TextInput(attrs={'class': 'form-control'})
)
cls = fields.MultipleChoiceField(
choices=models.Classes.objects.values_list('id', 'title'),
# 多选这个可不能删,因为下面的init修改的不是这里
widget=widgets.SelectMultiple(attrs={'class': 'form-control'})
) def __init__(self, *args, **kwargs):
super(TeacherForm, self).__init__(*args, **kwargs)
self.fields['cls'].widget.choices = models.Classes.objects.values_list('id', 'title') # form生成下拉框多选 + 动态刷新
- 多选
class TeacherForm(Form):
name = fields.CharField(max_length=16,
widget=widgets.TextInput(attrs={'class': 'form-control'})
)
cls = fields.MultipleChoiceField(
choices=models.Classes.objects.values_list('id', 'title'),
widget=widgets.SelectMultiple(attrs={'class': 'form-control'})
) def __init__(self, *args, **kwargs):
super(TeacherForm, self).__init__(*args, **kwargs)
self.fields['cls'].choices = models.Classes.objects.values_list('id', 'title') # form生成下拉框单选 + 动态刷新
class StudentForm(Form):
name = fields.CharField(max_length=16,
widget=widgets.TextInput(attrs={'class': 'form-control'})
)
cls = fields.ChoiceField(
choices=models.Classes.objects.values_list('id', 'title'),
widget=widgets.Select(attrs={'class': 'form-control'}) def __init__(self, *args, **kwargs):
super(StudentForm, self).__init__(*args, **kwargs)
self.fields['cls'].choices = models.Classes.objects.values_list('id', 'title') # form设置下拉框多选的默认值
- initial参数,值必须为字典。
row = models.teacher.objects.filter(id=nid).first()
obj = TeacherForm(initial={'name': row.name, 'cls': [1,2]}) # 在渲染编辑页面的时候,将obj传递给前端即可
# 前端 {{obj.name}} {{obj.cls}} # form设置下拉框单选的默认值
row = models.student.objects.filter(id=nid).first()
obj = StudentForm(
initial={
'name': row.name,
'age': row.age,
'email': row.email,
'cls_id': row.cls_id
} 字段太多,可以用另外一种比较好的方式:
row_dict = models.student.objects.filter(id=nid).values('id','age','email','cls_id')
obj = StudentForm(initial=row_dict) # Form生成常见的标签
class TestFieldForm(Form):
t1 = fields.CharField(
widget=widgets.TextInput
)
t2 = fields.CharField(widget=widgets.Textarea) # 结合起来就是,下拉框的单选, 单选用ChoiceField,
# widget是下拉框,radio, checkbox等
t3 = fields.ChoiceField(
choices=[(1, '男'), (2, '女')],
widget=widgets.Select()
) # 下拉框的多选
t4 = fields.MultipleChoiceField(
choices=[(1, '篮球'), (2, '足球'), (3, '控球')],
widget=widgets.SelectMultiple(attrs={}) ) # radio单选
t5 = fields.ChoiceField(
choices=[(1, '男'), (2, '女')],
widget=widgets.RadioSelect) # checkbox多选
t6 = fields.MultipleChoiceField(
choices=[(1, '篮球'), (2, '足球'), (3, '控球')],
widget=widgets.CheckboxSelectMultiple) # 上传文件类型
t7 = fields.FileField(
widget=widgets.FileInput
) def test_fields(request):
obj = TestFieldForm(initial={'t1': '姓名'})
return render(request, 'app01_test_fields.html', {'obj': obj}) # Form扩展--自定义数据验证
- RegxField
- 字段 = xxField(validators=)
- 钩子(增加一些高级验证)
a clean_xx -- 对特定的字段进行验证
b clean -- 对所有字段(或者两个或以上字段)进行验证 - 实例
# 自定义数据验证
from django.core.exceptions import ValidationError
class TestForm(Form):
user = fields.CharField(
widget=widgets.TextInput
)
pwd = fields.CharField(widget=widgets.PasswordInput)
email = fields.EmailField() age = fields.IntegerField(min_value=18,max_value=30) def clean_user(self):
# 增加验证用户名不能重复
# 这里不能获取密码的值即不能self.cleaned_data['pwd']
user_post = self.cleaned_data['user']
print(user_post)
if models.Student.objects.filter(name=user_post).count():
raise ValidationError('用户名已经存在')
else:
return self.cleaned_data['user'] def clean_pwd(self):
# 也可以自定制其他的验证
return self.cleaned_data['pwd'] def clean(self):
# 对字段间进行验证: 比如用户名和email不能重复
# 到了这里不能说明已经通过clean_xx验证,所以要用get age_post = self.cleaned_data.get('age')
print(age_post)
email_post = self.cleaned_data.get('email')
print(email_post)
if models.Student.objects.filter(age=age_post, email=email_post).count():
raise ValidationError('年龄和邮箱组合已经存在', code='com') return self.cleaned_data
# 视图
def test(request):
if request.method == 'GET':
obj = TestForm()
return render(request, 'app01_test.html',{'obj': obj} )
else:
obj = TestForm(request.POST)
if obj.is_valid():
print('合格')
else:
print('不合格')
com = obj.errors['__all__']
return render(request, 'app01_test.html',{'obj': obj, 'com':com}) # 前端
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/app01/test" method="POST">
{% csrf_token %}
{{ obj.user }}{{ obj.errors.user.0 }}
{{ obj.pwd }} {{ obj.errors.pwd.0 }}
{{ obj.email }} {{ obj.errors.email.0 }}
{{ obj.age }} {{ obj.errors.age.0 }}
{{ com }}
<input type="submit" value="提交">
</form>
</body>
</html> 总结:
1 使用
class FooForm(Form):
xx = fields.xxFields() def clean_xx(self): return self.cleaned_data['xx'] def clean()
return self.cleaned_data
2 页面展示
{{ obj.xx}} 3 后台
表单渲染
obj = FooForm(initial={'name':xx})
return render(request, 'xx.html', {'obj': obj})
表单提交
obj = FooForm()
if obj.is_valid():
# 插入数据库
return redirect()
else:
return render(request, 'xx.html', {'obj': obj})
2 补充自定义form里面的clean,__init__, clean_xx
def login_view(request):
#http://127.0.0.1:8000/alice/login/?next=/p/
redirect_to = request.POST.get("next", request.GET.get("next", ""))
print "alice: redirect to: "; redirect_to if request.method == 'POST':
form = LoginForm(request, data=request.POST) if form.is_valid():
mobile = form.cleaned_data['mobile']
#password = form.cleaned_data['password']
profile = Profile.objects.get(mobile=mobile)
login(request, form.get_user())
#redirect_to = '/p/' #alice: TBD: str(profile.id)
request.session['profile_id'] = profile.id
if redirect_to != '':
# Ensure the user-originating redirection url is safe.
if not is_safe_url(url=redirect_to, host=request.get_host()):
redirect_to = resolve_url(settings.LOGIN_REDIRECT_URL)
return HttpResponseRedirect(redirect_to)
else:
return JsonResponse({'status':'','id':profile.id, 'name':profile.personal_info.name})
else:
#print(form.errors.as_json())
json_str = form.errors.as_json()
return JsonResponse({'status':'', 'error': json_str}) else:
form = LoginForm()
return render(request, 'registration/login.html', {'form':form, 'next':redirect_to})
3 wupeiqi form总结网站
http://www.cnblogs.com/wupeiqi/articles/6144178.html
4 推荐一个学习form的中文网站
http://www.yiibai.com/django/django_form_processing.html