[oldboy-django][2深入django]Form总结
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
[oldboy-django][2深入django]Form总结的更多相关文章
- Django学习系列之Form基础
Django学习系列之Form基础 2015-05-15 07:14:57 标签:form django 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追 ...
- Django基础三(form和template)
上一篇博文学习了Django的View和urls,接下来是对django form 和 template的学习. 1 django form django form为我们提供了便捷的方式来创建一些HT ...
- {Django基础十之Form和ModelForm组件}一 Form介绍 二 Form常用字段和插件 三 From所有内置字段 四 字段校验 五 Hook钩子方法 六 进阶补充 七 ModelForm
Django基础十之Form和ModelForm组件 本节目录 一 Form介绍 二 Form常用字段和插件 三 From所有内置字段 四 字段校验 五 Hook钩子方法 六 进阶补充 七 Model ...
- Django基础十之Form和ModelForm组件
一 Form介绍 我们之前在HTML页面中利用form表单向后端提交数据时,都会写一些获取用户输入的标签并且用form标签把它们包起来. 与此同时我们在好多场景下都需要对用户的输入做校验,比如校验用户 ...
- 12.Django基础十之Form和ModelForm组件
一 Form介绍 我们之前在HTML页面中利用form表单向后端提交数据时,都会写一些获取用户输入的标签并且用form标签把它们包起来. 与此同时我们在好多场景下都需要对用户的输入做校验,比如校验用户 ...
- day 64 Django基础十之Form和ModelForm组件
Django基础十之Form和ModelForm组件 本节目录 一 Form介绍 二 Form常用字段和插件 三 From所有内置字段 四 字段校验 五 Hook钩子方法 六 进阶补充 七 Mod ...
- Django组件(五) Django之ContentType组件
基础使用 -contenttype组件 -django提供的一个快速连表操作的组件,可以追踪项目中所有的APP和model的对应关系,并记录在ContentType表中. 当我们的项目做数据迁移后,会 ...
- Django框架02 /Django下载安装、url路由分发
Django框架02 /Django下载安装.url路由分发 目录 Django框架02 /Django下载安装.url路由分发 1. django下载安装 2. pycharm创建项目 3. 基于D ...
- 五步教你实现使用Nginx+uWSGI+Django方法部署Django程序
Django的部署可以有很多方式,采用nginx+uwsgi的方式是其中比较常见的一种方式. 在这种方式中,我们的通常做法是,将nginx作为服务器最前端,它将接收WEB的所有请求,统一管理请求.ng ...
- 【Django】Django 如何使用 Django设置的日志?
代码: from django.core.management.base import BaseCommand, CommandError from django.db import models # ...
随机推荐
- js实现排序去重计算字符次数
/*去重*/ var arr=[1,4,4,7,3,9,0,3,2,1,"你好","你","你好","你 "]; var ...
- linux 命令——56 netstat(转)
netstat命令用于显示与IP.TCP.UDP和ICMP协议相关的统计数据,一般用于检验本机各端口的网络连接情况.netstat是在内核中访问网络及相关信息的程序,它能提供TCP连接,TCP和UDP ...
- 可以让表格变为拖动边界调整列宽度的JS办法
需要引入文件: jquery.resizableColumns.min.js 和 jquery.resizableColumns.css 代码如下: $(".tableclass" ...
- POJ 3041 Asteroids (对偶性,二分图匹配)
题目:POJ 3041 Asteroids http://poj.org/problem?id=3041 分析: 把位置下标看出一条边,这显然是一个二分图最小顶点覆盖的问题,Hungary就好. 挑战 ...
- noip模拟赛#39
昨晚打开的题想了一会发现都不会后决定慢慢想.然后早上开校会的时候莫名其妙的都想出来了... T1:m=100,ai=50000,i<=5.1到m的数每个数只能用一次,判断是否能够有这些数的某些数 ...
- 分析ELF的加载过程
http://blog.chinaunix.net/uid-72446-id-2060538.html 对于可执行文件来说,段的加载位置是固定的,程序段表中如实反映了段的加载地址.对于共享库来?段的加 ...
- python_9_for
#1 for i in range(10):#默认从0开始,步长为1 print("loop",i) #2 for i in range(0,10,1):#步长为1 print(& ...
- 适配iOS10和Xcode8
1.权限设置 iOS10,访问系统权限需要在info.plist中注册,否则直接crash! 注意,Value值不可为空,否则会被Appstore拒掉! 2.Notification,学习资料 喵神总 ...
- MySQL基础 - 1 数据库基础
一.数据库基础 1.什么是数据库 1.数据库(database)是保存有组织的数据的容器( 通常是一个文件或一组文件 ) 2.数据库是一个以某种有组织的方式存储的数据集合 注意:数据库软件应该称为DB ...
- 七、Shell printf 命令
Shell printf 命令 上一章节我们学习了 Shell 的 echo 命令,本章节我们来学习 Shell 的另一个输出命令 printf. printf 命令模仿 C 程序库(library) ...