Django Form组件

From的功能实际有三个,Form验证,From添加标签,Form保留返回值

保留返回值的功能还可用ajax发送

一、Form验证

对用户的请求的数据进行验证

使用类

从前端获取的数据的name属性的名字要和定义的class类中的字段一致

1
2

二、From添加标签

1 自己在前端写标签

自己在前端写input标签,在提交后在后台获取name属性的值进行验证,user要和LoginForm 中的字段名一致
之后obj.is_valid()会自动验证,验证通过(通过后正确信息是obj.cleaned_data,
),返回True,不通过返回false(错误信息obj.errors),通常在前端获取,如obj.errors.user.0

这里的问题是提交后不能保存信息

1
2
3
4
5
6
7
8
9
10
11
<form id="f1" method="POST" action="/login/">
{% csrf_token %}
<p>
用户名:<input type="text" name="user">{{ obj.errors.user.0}}
</p>
<p>
密码:<input type="password" name="pwd">{{ obj.errors.pwd.0 }}
</p>
<input type="submit" value="提交">
<a href="" onclick="submitForm();">ajax提交</a>
</form>

2 Form在前端生成标签

在前端生成的关键是在提交GET请求的时候传递obj到前端,这个obj是LoginForm实例化的对象,内部不加参数

1
2
3
4
5
6
7
8
9
10
11
def login(request):
if request.method == "GET":
obj = LoginForm() # 此处的obj不加参数
return render(request, 'login.html', {'obj': obj})
return render(request, 'login.html')
else:
obj = LoginForm(request.POST)
if obj.is_valid():
print(obj.cleaned_data)
return redirect('http://www.baidu.com')
return render(request, 'login.html', {'obj': obj})

前端代码

第一个obj.user是Form生成的标签,第二个是post请求显示的错误信息

1
2
3
4
5
6
7
8
9
10
11
<form id="f1" method="POST" action="/login/">
{% csrf_token %}
<p>
用户名:{{ obj.user }}{{ obj.errors.user.0}}
</p>
<p>
密码:{{ obj.pwd }}{{ obj.errors.pwd.0 }}
</p>
<input type="submit" value="提交">
<a href="" onclick="submitForm();">ajax提交</a>
</form>

三、Form保留返回值

通过from生成标签能够保留表单已经填的通过验证的信息,不能验证通过的不保留

密码的位置加上了widget=widgets.PasswordInput(),输入的密码就是隐藏的

1 定义Form类,继承Form

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
from django.shortcuts import render, redirect, HttpResponse
from django.forms import Form
from django.forms import fields
from django.forms import widgets
import json
class LoginForm(Form):
user = fields.CharField(
max_length=18,
min_length=6,
required=True,
error_messages={
'max_length': '用户名太长了',
'min_length': '用户名太短了',
'required': '用户名不能为空',
},
)
pwd = fields.CharField(
min_length=16,
required=True,
error_messages={
'min_length': '密码太短了',
'required': '密码不能为空',
},
widget=widgets.PasswordInput(), # 密码格式,将输入的内容隐藏
)

2 GET请求和POST请求都发送obj

GET请求的时候需要实例化一个对象obj = LoginForm()

1
2
3
4
5
6
7
8
9
10
def login(request):
if request.method == "GET":
obj = LoginForm() # 此处的obj不加参数
return render(request, 'login.html', {'obj': obj})
else:
obj = LoginForm(request.POST)
if obj.is_valid():
print(obj.cleaned_data)
return redirect('http://www.baidu.com')
return render(request, 'login.html', {'obj': obj})

3 前端

现在的浏览器默认会在前端进行验证,在测试的时候加上novalidate,之后删除

1
2
3
4
5
6
7
8
9
10
<form id="f1" method="POST" action="/login/" novalidate>
{% csrf_token %}
<p>
用户名:{{ obj.user }}{{ obj.errors.user.0}}
</p>
<p>
密码:{{ obj.pwd }}{{ obj.errors.pwd.0 }}
</p>
<input type="submit" value="提交">
</form>

ajax保留返回值

1 后台代码

关键点:

  • 设置字典,没有通过验证把status设置成false并把obj.errors通过json发送到后台
1
2
3
4
5
6
7
8
9
10
11
12
def ajax_login(request):
ret = {'status': True, 'msg': None}
obj = LoginForm(request.POST)
if obj.is_valid():
print(obj.cleaned_data)
else:
# print(obj.errors)
ret['status'] = False
ret['msg'] = obj.errors
v = json.dumps(ret) # 把错误的数据发送到后台
return HttpResponse(v)

2 前端代码

  • 在绑定事件的a标签上不要加href,自己点击的时候会默认刷新
  • 点击的时候清除上一次的错误信息
  • ajax的data发送的的时候会把字典打包成字符串。通过ajax的serialize
    打包获取数据
  • 错误信息的内容是通过字符串拼接
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<form id="f1" method="POST" action="/login/" novalidate>
{% csrf_token %}
<p>
用户名:{{ obj.user }}{{ obj.errors.user.0}}
</p>
<p>
密码:{{ obj.pwd }}{{ obj.errors.pwd.0 }}
</p>
<input type="submit" value="提交">
<a onclick="submitForm();">ajax提交</a>
</form>
<script src="/static/jquery-3.2.1.js"></script>
<script>
function submitForm() {
$('.c1').remove();//每次点击清除上一次的错误信息
$.ajax({
url: '/ajax_login/',
type: 'POST',
data: $('#f1').serialize(),//获取form的所有数据并打包 包含了csrf的随机字符串
dataType: 'JSON',
success: function (arg) {
console.log(arg);
if (arg.status) {
} else {
$.each(arg.msg, function (index, value) {
console.log(index, value);
var tag = document.createElement('span');
tag.innerHTML = value[0];
tag.className = 'c1';//给错误信息添加class
$('#f1').find('input[name="' + index + '"]').after(tag);
/*拼接字符串*/
})
}
}
})
}
</script>

四、使用form实现学生、班级、教师管理

1 班级管理

1.1 班级form验证

班级的名字以班级开头 后面加数字

1
2
3
4
5
6
7
8
9
10
11
12
13
from django.shortcuts import render, redirect, HttpResponse
from django.forms import Form
from django.forms import fields
from django.forms import widgets
import json
from app01 import models
class ClassForm(Form):
"""
添加班级的验证规则 以班级开头
"""
title = fields.RegexField('班级\d+') # 这个名字和数据库的字段名一致

1.2 班级列表

从数据库获取班级的全部信息,并发送到前端页面

1
2
3
def class_list(request):
cls_list = models.Classes.objects.all()
return render(request, 'class_list.html', {'cls_list': cls_list})

班级列表前端是直接循环列出班级列表,row.title

1
2
3
4
5
6
7
8
9
10
<h1>班级列表</h1>
<div>
<a href="/add_class/">添加班级</a>
</div>
<ul>
{% for row in cls_list %}
<li>{{ row.title }}</li>
{% endfor %}
</ul>

1.3 添加班级列表

  • obj = ClassForm() # 创建标签
  • 通过验证后 obj.cleaned_data 是字典类型
  • 开始定义和数据库中的字段名字一致,直接在下面用**对字典的数据进行接收
  • 没有通过验证返回错误的信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def add_class(request):
if request.method == "GET":
obj = ClassForm() # 创建标签
return render(request,'add_class.html',{'obj':obj})
else:
obj = ClassForm(request.POST)
if obj.is_valid():
"""
通过验证后 obj.cleaned_data 是字典类型
在数据库添加一条数据
models.Classes.objects.create(title=obj.cleaned_data['xxx'])
如果字段的名字是xxx 就需要自己去取出来
所以开始定义和数据库中的字段名字一致,直接在下面用**对字典的数据进行接收
"""
print(obj.cleaned_data)
models.Classes.objects.create(**obj.cleaned_data) # 使用**接收
return redirect('/class_list/')
return render(request,'add_class.html',{'obj':obj}) # 没有通过验证返回错误的信息

前端:

  • obj.title 创建标签
  • obj.errors.title.0 显示错误信息
1
2
3
4
5
6
7
<h1>添加班级</h1>
<form method="POST" action="/add_class/">
{% csrf_token %}
{{ obj.title }} {{ obj.errors.title.0 }}
{# {{ obj }}#}
<input type="submit" value="提交">
</form>

1.4 编辑班级

  • url 中的正则加上(\d+)
  • 页面的nid 是通过在class_list上点击的时候获取的,把row.id传递
  • <a class="btn btn-primary" href="/edit_class/">编辑</a>
  • 在后台函数中用参数nid进行接收
  • 使用initial进行参数传递
  • 编辑是用update对数据库进行更新
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def edit_class(request, nid):
"""
nid 负责接收 class页面中的row.id中的值
在URL中设置edit_class/(\d+)/
:param request:
:param nid:
:return:
"""
if request.method == "GET":
row = models.Classes.objects.filter(id=nid).first()
# obj = ClassForm(initial={'title': row.title}) # 这里是显示默认值 默认的是data 会进行验证 initial 不会进行验证
obj = ClassForm(initial={'title': row.title})
return render(request, 'edit_class.html', {'nid': nid, 'obj': obj})
else:
obj = ClassForm(request.POST)
if obj.is_valid():
models.Classes.objects.filter(id=nid).update(**obj.cleaned_data)
return redirect('/class_list/')
return render(request, 'edit_class.html', {'obj': obj})

2 学生管理

2.1学生管理的后端

  • 单选的时候用的而是插件Select中的choice
  • choice中的参数是列表,列表中的元素是元组
  • value_list操作中获得是元组对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class StudentForm(Form):
name = fields.CharField(
min_length=2,
max_length=6,
# widget = widgets.TextInput(attr)
)
email = fields.EmailField()
age = fields.IntegerField(min_value=18, max_value=100)
cls_id = fields.IntegerField(
# widget = widgets.Select(choices=[(1,'上海'),(1,'北京')]) # choices = 列表的内部是元组的形式
widget=widgets.Select(choices=models.Classes.objects.values_list('id', 'title'))
# choice 是应用于单选 value_list 获取的就是列表中有元组
# 注意此处有bug 添加班级后在这里没有新添加的班级,下面进行修复
)
def student_list(request):
stu_list = models.Students.objects.all()
return render(request, 'student_list.html', {'stu_list': stu_list})
def add_student(request):
if request.method == "GET":
obj = StudentForm()
return render(request, 'add_student.html', {'obj': obj})
else:
obj = StudentForm(request.POST)
if obj.is_valid():
models.Students.objects.create(**obj.cleaned_data)
return redirect('/student_list/')
return render(request, 'add_student.html', {'obj': obj})

BUG修复 动态更新数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
cls_id = fields.IntegerField(
# widget = widgets.Select(choices=[(1,'上海'),(1,'北京')]) # choices = 列表的内部是元组的形式
# widget=widgets.Select(choices=models.Classes.objects.values_list('id', 'title'),
# attrs={'class': 'form-control'})
# choice 是应用于单选 value_list 获取的就是列表中有元组
# 注意此处有bug 添加班级后在这里没有新添加的班级,下面进行修复
# 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_id'].widget.choices = models.Classes.objects.values_list('id', 'title') # 动态查询
# 注意单选的时候用的而是widget.choice
# 后面的多选没有用

2.2 前端处理

  • 有外键的可以直接点出来 row.cls.title

学生列表

1
2
3
4
5
6
7
8
9
10
<h1>学生列表</h1>
<a href="/add_student/">添加</a>
<ul>
{% for row in stu_list %}
<li>
{{ row.name }}-{{ row.email }}-{{ row.age }}-{{ row.cls_id }}-{{ row.cls.title }}
<a href="/edit_student/{{ row.id }}">编辑</a>
</li>
{% endfor %}
</ul>

添加学生,标签通过后台生成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<h1>添加学生</h1>
<form action="/add_student/" method="POST">
{% csrf_token %}
<p>
姓名:{{ obj.name }} {{ obj.errors.name.0 }}
</p>
<p>
邮箱: {{ obj.email }} {{ obj.errors.email.0 }}
</p>
<p>
年龄: {{ obj.age }} {{ obj.errors.age.0 }}
</p>
<p>
班级 {{ obj.cls_id }} {{ obj.errors.cls_id.0 }}
</p>
<input type="submit" value="提交">
</form>

编辑学生

  • 通过student_list 中获取了row.id
  • row.id 传递到后台
  • initial接收的是字典对象
  • 正好values获取的是字典对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def edit_student(request,nid):
"""
获取编辑学生的的信息并展现在输入框中 使用initial
:param request:
:param nid:
:return:
"""
if request.method == "GET":
row = models.Students.objects.filter(id=nid).values(
'name','email','age','cls_id'
).first() # 使用valuse和first获取的是字典对象
obj = StudentForm(initial=row) # row接收的是字典对象
return render(request,'edit_student.html',{'nid':nid,'obj':obj})
else:
obj = StudentForm(request.POST)
if obj.is_valid():
models.Students.objects.filter(id=nid).update(**obj.cleaned_data)
return redirect('/student_list/')
return render(request,'edit_student.html',{'obj':obj})

前端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="/static/plugins/bootstrap-3.3.7-dist/css/bootstrap.css">
</head>
<body>
<div style="width: 500px; margin: 80px auto;">
<form method="POST" action="/edit_student/{{ nid }}/">
{% csrf_token %}
<div class="form-group">
<label for="exampleInputEmail1">姓名</label>
{{ obj.name }}
</div>
<div class="form-group">
<label for="exampleInputPassword1">邮箱</label>
{{ obj.email }}
</div>
<div class="form-group">
<label for="exampleInputPassword1">年龄</label>
{{ obj.age }}
</div>
<div class="form-group">
<label for="exampleInputPassword1">班级</label>
{{ obj.cls_id }}
</div>
<input type="submit" value="提交">
</form>
</div>
</body>
</html>

3 教师管理

form的执行过程:

1.找到所有的的字段 tname xx(这些都是静态字段)
2.把字段放到self.fields中{}是字典的形式
3.程序从上到下执行,启动后从数据库获取一次数据,然后就不变了,导致数据不能动态刷新
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
def teacher_list(request):
tea_list = models.Teacher.objects.all()
return render(request, 'teacher_list.html', {'tea_list': tea_list})
class TeacherForm(Form):
"""
form的执行过程:
1.找到所有的的字段 tname xx(这些都是静态字段)
2.把字段放到self.fields中{}是字典的形式
3.程序从上到下执行,启动后从数据库获取一次数据,然后就不变了,导致数据不能动态刷新
"""
tname = fields.CharField(min_length=2)
xx = fields.MultipleChoiceField(
# choice=models.Classes.objects.values_list('id', 'title'), # 启动的时候查询
widget=widgets.SelectMultiple
)
"""
自己重写的方法
1.从self.fields的字典中找xx,然后找xx的choice,choice=[(),()]的形式
2.源码中会循环所有的字段,把有Field的加入self。Fields中,因为都是继承Fields
"""
def __init__(self, *args, **kwargs):
super(TeacherForm, self).__init__(*args, **kwargs)
self.fields['xx'].choices = models.Classes.objects.values_list('id', 'title') # 动态查询 多选的时候不用widget

添加老师

1
2
3
4
5
6
7
8
9
10
11
12
def add_teacher(request):
if request.method == "GET":
obj = TeacherForm()
return render(request, 'add_teacher.html', {'obj': obj})
else:
obj = TeacherForm(request.POST)
if obj.is_valid():
xx = obj.cleaned_data.pop('xx') # 把验证通过的数据删除班级ID的信息 只留下教师表中的信息
row = models.Teacher.objects.create(**obj.cleaned_data) # 向教师表添加数据 row代表教师表的对象
row.c2t.add(*xx) # [1,2] 通过教师表 向ManyTomany的第三张表中添加数据 班级ID是列表的形式 用*接收
return redirect('/teacher_list/')
return render(request, 'add_teacher.html', {'obj': obj})

编辑老师

打开的时候有默认值, obj = TeacherForm(initial={‘tname’: row.tname, ‘xx’: id_list}) # 显示的默认值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def edit_teacher(request, nid):
if request.method == "GET":
row = models.Teacher.objects.filter(id=nid).first()
class_ids = row.c2t.values_list('id')
print(class_ids)
# for
# 可以用for循环把class_ids中取出来
# id_list =[]
# for i in class_ids:
# id_list.append(i[0]) # calss_ids是[(1,),(2)]
# print(id_list)
# zip + 三元运算
# list(zip(*class_ids))[0] #<QuerySet [(1,), (2,)]>
id_list = list(zip(*class_ids))[0] if list(zip(*class_ids)) else [] # 可能为空的情况
obj = TeacherForm(initial={'tname': row.tname, 'xx': id_list}) # 显示的默认值 xx是班级列表[1,2,3]
return render(request, 'edit_teacher.html', {'obj': obj})

生成标签

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>编辑老师</h1>
<form action="">
教师姓名 <p>{{ obj.tname }}</p>
任教班级<p> {{ obj.xx }}</p>
</form>
</body>
</html>

总结

Select框:

单选:单选是用CharField IntegerField 和Select搭配
ChoiceField也是用于单选框,但是choice写在外面

cls_id = fields.IntegerField(
    # widget=widgets.Select(choices=[(1,'上海'),(2,'北京')])
    widget=widgets.Select(choices=models.Classes.objects.values_list('id','title'),attrs={'class': 'form-control'})
)

cls_id = fields.ChoiceField(
    choices=models.Classes.objects.values_list('id','title'),
    widget=widgets.Select(attrs={'class': 'form-control'})
)


obj = FooForm({'cls_id':1})

多选:

多选只能用MultipleChoiceField ,且choices放在外面。最后重写init方法后就不用了

xx = fields.MultipleChoiceField(
    choices=models.Classes.objects.values_list('id','title'),
    widget=widgets.SelectMultiple
)

obj = FooForm({'cls_id':[1,2,3]})

通过Bootstrap进行界面美化

bootstrap 的关键是给相应的标签添加class属性

如:添加form-control

1
2
3
<p>
测试:<input type="text" class="form-control"> {# 使用bootstrap #}
</p>

对于自己生成的标签,是通过Form组件的插件添加属性的 widgets

attrs后面对应的是字典类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class StudentForm(Form):
name = fields.CharField(
min_length=2,
max_length=6,
widget = widgets.TextInput(attrs={'class':'form-control'}) # 给输入框添加属性
)
email = fields.EmailField(
widget=widgets.TextInput(attrs={'class': 'form-control'}) # 给输入框添加属性
)
age = fields.IntegerField(
min_value=18, max_value=100,
widget = widgets.TextInput(attrs={'class': 'form-control'}) # 给输入框添加属性
)
cls_id = fields.IntegerField(
# widget = widgets.Select(choices=[(1,'上海'),(1,'北京')]) # choices = 列表的内部是元组的形式
widget=widgets.Select(choices=models.Classes.objects.values_list('id', 'title'),attrs={'class': 'form-control'})
# choice 是应用于单选 value_list 获取的就是列表中有元组
# 注意此处有bug 添加班级后在这里没有新添加的班级,下面进行修复
)

© 2018 Peter's Blog Center All Rights Reserved.
Theme by hiero