[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 # ...
随机推荐
- Localroast使用总结
全手打原创,转载请标明出处: https://www.cnblogs.com/dreamsqin/p/10883248.html,多谢~=.= 什么是Localroast 一个根据 JSON 文件快速 ...
- OpenGL位图变形问题
因为初次接触OpenGL,图形学也后悔当初在学校没有认真学,隐约记得教授当时讲过图像变形的问题,而且我的bitmap也是2的N次方:16*16的,在网络上找到的大多都是一句话:“视口的纵横比一般和视景 ...
- Nginx源码安装及调优配置(转)
导读 由于Nginx本身的一些优点,轻量,开源,易用,越来越多的公司使用nginx作为自己公司的web应用服务器,本文详细介绍nginx源码安装的同时并对nginx进行优化配置. Nginx编译前 ...
- WARNING: The TCP backlog setting of 511.解决
redis启动警告问题:WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/so ...
- 【Python】bytes和hex字符串之间的相互转换。
反复在几个环境上折腾码流的拼装解析和可读化打印,总是遇到hex字符串和bytes之间的转换,记录在这里吧. 1. 在Python2.7.x上(更老的环境真心折腾不起),hex字符串和bytes之间的转 ...
- axiospost请求向后端提交数据
Axios向后端提交数据容易接收不到原因是传参方式是request payload,参数格式是json,而并非用的是form传参,所以在后台用接收form数据的方式接收参数就接收不到了.post表单请 ...
- css与html结合四种方式
方式一:每个标签加一个属性法 <div style="background-color:red;color:green;"></div> 方式二:head中 ...
- linux下通过phpize为php在不重新编译php情况下安装模块memcache
通过phpize为php在不重新编译php情况下安装模块memcache 1. 下载 wget http://pecl.php.net/get/memcache-2.2.4.tgz 解压 ...
- mysql主从复制及双主复制
之前做过一次在单台机器上的多实例的mysql,这次分开做,使用两台主机. 这里使用的主机地址分别为: MASTER:192.168.214.135 SLAVE : 192.168.214.128 这 ...
- Linux ps与top命令
Linux ps与top命令 这两个命令都是查看系统进程信息的命令,但是用处有点儿不同 1.ps命令--提供系统过去信息的一次性快照 也就是说ps命令能够查看刚刚系统的进程信息 命令:ps aux或 ...