跟b站武沛齐老师的视频学习
对学习过程进行一些记录以便复习 同时自我督促 :D冲鸭视频链接: 15天django入门到放弃-哔哩哔哩.
本篇均为 Form 相关内容
Form 验证整理
- 用户提交数据进行校验
- Form 提交(刷新,失去上次内容)
a. LoginForm(Form)
字段名 = xxxx.xxFields() # 本质验证规则,正则表达式
字段名 = xxxx.xxFields() # 本质验证规则,正则表达式
字段名 = xxxx.xxFields() # 本质验证规则,正则表达式
字段名 = fiedls.RegexField(‘正则表达式’, error_messages={xx})
b. obj = LoginForm(用户提交的数据)
c. result = obj.is_valid()
d. obj.cleaned_data
e. obj.errors
- 内部原理
1. LoginForm 实例化时,
self.fields={
‘user’: 正则表达式
‘pwd’: 正则表达式
}
2. 循环self.fields
flag = true
errors
cleaned_data
for k, v in self.fields.items():
# k是: user, pwd
# v是: 正则表达式
# 1. user, 正则表达式
input_value = request.POST.get(k)
正则表达式和input_value不匹配
flag = False
return flag
- Ajax 提交(不刷新,上次内容自动保留)
PS:Ajax 提交 > Form 提交
总结:
class Foo(Form):
字段 = 正则表达式
字段 = 自定义正则表达式
- 保留上次输入内容
ajax提交 例
views.py
:
from django.shortcuts import render, redirect, HttpResponse
from django.forms import Form, fields
import json
class LoginForm(Form):
username = fields.CharField(required=True)
password = fields.CharField(min_length=18)
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)
前端收到的v:{“status”: false, “msg”: {“password”: [“Ensure this value has at least 18 characters (it has 12).”]}}
在每个组件后添加提示的办法:
var tag = document.createElement('span');
tag.innerHTML = value[0];
tag.className = 'c1';
$('#f1').find('input[name="' + index + '"]').after(tag);
为了使每次重新提交时之前的 tag 消失,为它添加 c1
类属性,然后再请求处理开头加上 $('.c1').remove();
;
代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>用户登录</h1>
<form id='f1' method="POST" action="/login/">
{% csrf_token %}
<p>
用户:<input type="text" name="username"> {{ obj.errors.username.0 }}
</p>
<p>
密码:<input type="password" name="password"> {{ obj.errors.password.0 }}
</p>
<a onclick="submitForm();">提交</a>
</form>
<script src="http://ajax.aspnetcdn.com/ajax/jquery/jquery-1.12.4.min.js"></script>
<script>
function submitForm(){
$.ajax({
url: '/ajax_login/',
type: 'POST',
data: $('#f1').serialize(),
dataType: 'JSON',
success: function (arg) {
$('.c1').remove();
console.log(arg);
if (arg.status){
} else {
$.each(arg.msg, function (index, value) {
console.log(value);
var tag = document.createElement('span');
tag.innerHTML = value[0];
tag.className = 'c1';
$('#f1').find('input[name="' + index + '"]').after(tag);
})
}
}
})
}
</script>
</body>
</html>
相关参数(生成HTML标签)
widget=None, HTML插件
label=None, 用于生成Label标签或显示内容
initial=None, 初始值
help_text='', 帮助信息(在标签旁边显示)
disabled=False, 是否可以编辑
label_suffix=None Label内容后缀
(自动生成 HTML 标签)
required=True, 是否允许为空
error_messages=None, 错误信息 {'required': '不能为空', 'invalid': '格式错误'}
show_hidden_initial=False, 是否在当前插件后面再加一个隐藏的且具有默认值的插件(可用于检验两次输入是否一直)
validators=[], 自定义验证规则
localize=False, 是否支持本地化
示例:
views.py
:
class TestForm(Form):
t1 = fields.CharField(
required=True,
label='用户名',
help_text='helptext',
initial='initial',
disabled=True,
label_suffix=':',
# 只推荐这个:
widget=forms.Textarea,
)
def test(request):
obj = TestForm()
return render(request, 'test.html', {'obj': obj})
test.html
:
<form action="/test/" method="POST">
{% csrf_token %}
<p>
{{ obj.t1.label }}
{{ obj.t1 }}
{{ obj.t1.help_text }}
</p>
{{ obj.as_p }}
</form>
![](https://img-blog.csdnimg.cn/fd87968c85db40dca25c8e2fb9c839e9.png)
保留上次输入内容
- Ajax 仅用验证功能
- Form 验证功能 + 生成 HTML 标签
obj = TestForm(request.POST)
示例
班级管理
models.py
:
class Classes(models.Model):
title = models.CharField(max_length=32)
views.py
:
class ClassForm(Form):
title = fields.RegexField('1618\d+')
def class_list(request):
cls_list = models.Classes.objects.all()
return render(request, 'class_list.html', {'cls_list': cls_list})
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():
models.Classes.objects.create(**obj.cleaned_data)
return redirect('/class_list/')
else:
return render(request, 'add_class.html', {'obj': obj})
def edit_class(request, nid):
if request.method == "GET":
row = models.Classes.objects.filter(id=nid).first()
# 让页面显示初始值
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/')
else:
return render(request, 'edit_class.html', {'nid': nid, 'obj': obj})
class_list.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>班级列表</h1>
<div>
<a href="/add_class/">添加</a>
</div>
<ul>
{% for row in cls_list %}
<li>{{ row.title }} <a href="/edit_class/{{ row.id }}/">编辑</a> </li>
{% endfor %}
</ul>
</body>
</html>
add_list.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>添加班级</h1>
<form method="POST" action="/add_class/" novalidate>
{% csrf_token %}
<p>
{{ obj.title }}
{{ obj.title.errors.0 }}
</p>
<input type="submit" value="提交">
</form>
</body>
</html>
注意,编辑班级对应的 url 为:re_path(r'^edit_class/(\d+)/', views.edit_class),
;
edit_class.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>编辑班级</h1>
<form method="POST" action="/edit_class/{{ nid }}/">
{% csrf_token %}
<p>
{{ obj.title }}
{{ obj.title.errors.0 }}
</p>
<input type="submit" value="提交">
</form>
</body>
</html>
学生管理
models.py
:
class Student(models.Model):
name = models .CharField(max_length=32)
email = models.CharField(max_length=32)
age = models.IntegerField(max_length=32)
cls = models.ForeignKey('Classes', on_delete=models.DO_NOTHING)
views.py
:
class StudentForm(Form):
name = fields.CharField(min_length=2, max_length=6)
email = fields.EmailField()
age = fields.IntegerField(min_value=18, max_value=25)
cls_id = fields.IntegerField(
widget=widgets.Select(choices=models.Classes.objects.values_list('id', 'title'))
)
def student_list(request):
stu_list = models.Student.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.Student.objects.create(**obj.cleaned_data)
return redirect('/student_list/')
else:
return render(request, 'add_student.html', {'obj': obj})
def edit_student(request, nid):
if request.method == "GET":
# 此处直接取到一个字典,方便初始化,values要写在first前面,因为first不能对values得到的字典做操作
row = models.Student.objects.filter(id=nid).values('name', 'email', 'age', 'cls_id').first()
# 让页面显示初始值
obj = StudentForm(initial=row)
return render(request, 'edit_student.html', {'nid': nid, 'obj': obj})
else:
obj = StudentForm(request.POST)
if obj.is_valid():
models.Student.objects.create(**obj.cleaned_data)
return redirect('/student_list/')
else:
return render(request, 'edit_student.html', {'obj': obj, 'nid': nid})
student_list.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<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>
</body>
</html>
add_student.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>添加学生</h1>
<form action="/add_student/" method="POST">
{% csrf_token %}
<p>
用户名:{{ obj.name }}
</p>
<p>
邮箱:{{ obj.email }}
</p>
<p>
年龄:{{ obj.age }}
</p>
<p>
班级:{{ obj.cls_id }}
</p>
<input type="submit" value="提交">
</form>
</body>
</html>
edit_student.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>编辑学生</h1>
<form method="POST" action="/edit_student/{{ nid }}/" novalidate>
{% csrf_token %}
<p>
用户名:{{ obj.name }} {{ obj.name.errors.0 }}
</p>
<p>
邮箱:{{ obj.email }} {{ obj.email.errors.0 }}
</p>
<p>
年龄:{{ obj.age }} {{ obj.age.errors.0 }}
</p>
<p>
班级:{{ obj.cls_id }} {{ obj.cls_id.errors.0 }}
</p>
<input type="submit" value="提交">
</form>
</body>
</html>
教师管理
多选:
xx = fields.CharField(
widget=widgets.Select(
choices=models.Classes.objects.values_list('id', 'title'),
attrs={'multiple': 'multiple'}
),
)
使用这种方法是不能达到多选的目的的,因为 Select
的实现是用 request.get('')
,只能拿到一个数据,应该用 SelectMultiple
。
拿到数据格式为:
{'tname': 'xxx', 'xx': "['1', '2']"}
仍然不好处理,改为使用 MultipleChoiceFields
:
xx = fields.MultipleChoiceField(
choices=models.Classes.objects.values_list('id', 'title')
)
总结:
单选:CharFields + Select
ChoiceFields + choices
多选:MultipleChoice + choices
编辑老师中,实现初始化:
row = models.Teacher.objects.filter(id=nid).first()
class_ids = row.c2t.values_list('id')
得到数据格式为:<QuerySet [(1,), (2,)]>
zip 方法:
因此加上:
id_list = list(zip(*class_ids))[0] if list(zip(*class_ids)) else []
obj = TeacherForm(initial={'tname': row.tname, 'xx': id_list})
这样的到的数据形式为:(1, 2)
代码:
models.py
:
class Teacher(models.Model):
tname = models.CharField(max_length=32)
c2t = models.ManyToManyField('Classes')
views.py
:
def teacher_list(request):
tea_list = models.Teacher.objects.all()
return render(request, 'teacher_list.html', {'tea_list': tea_list})
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():
print(obj.cleaned_data)
xx = obj.cleaned_data.pop('xx')
row = models.Teacher.objects.create(**obj.cleaned_data)
row.c2t.add(*xx)
return redirect('/teacher_list/')
else:
print(obj.errors)
return render(request, 'add_teacher.html', {'obj': obj})
def edit_teacher(request, nid):
if request.method == "GET":
row = models.Teacher.objects.filter(id=nid).first()
class_ids = row.c2t.values_list('id')
id_list = list(zip(*class_ids))[0] if list(zip(*class_ids)) else []
print(id_list)
obj = TeacherForm(initial={'tname': row.tname, 'xx': id_list})
return render(request, 'edit_teacher.html', {'obj': obj, 'nid': nid})
else:
pass
teacher_list
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>教师列表</h1>
<div>
<a href="/add_teacher/">添加</a>
</div>
<table border="1">
<thead>
<tr>
<th>ID</th>
<th>姓名</th>
<th>任教班级</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for row in tea_list %}
<tr>
<td>{{ row.id }}</td>
<td>{{ row.tname }}</td>
<td>
{% for item in row.c2t.values %}
{{ item.title }}
{% endfor %}
</td>
<td><a href="/edit_teacher/{{ row.id }}/">编辑</a> </td>
</tr>
{% endfor %}
</tbody>
</table>
</body>
</html>
add_teacher.html
;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form method="POST" action="/add_teacher/">
{% csrf_token %}
<p>
姓名:{{ obj.tname }}
</p>
<p>
班级:{{ obj.xx }}
</p>
<input type="submit" value="提交">
</form>
</body>
</html>
edit_teacher.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>编辑老师</h1>
<form method="POST" action="/edit_teacher/{{ nid }}">
{% csrf_token %}
{{ obj.tname }}
{{ obj.xx }}
</form>
</body>
</html>
数据无法动态显示
TeacherForm
进行了一次对象实例化,生成一个对象,不再更新。导致 Class
中添加了新的班级,增加教师页面的班级选择并未更新。
解决办法:为 TeacherForm
类增加 __init__
方法
def __init__(self, *args, **kwargs):
super(TeacherForm, self).__init__(*args, **kwargs)
self.fields['xx'].widget.choices = models.Classes.objects.values_list('id', 'title')
插件及样式定制
在 views.py
中添加样式后传给前端:
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=25, 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'})
)
引用 bootstrap:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="/static/plugins/bootstrap-3.4.1-dist/css/bootstrap.css"/>
<link rel="stylesheet" href="/static/plugins/bootstrap-3.4.1-dist/css/bootstrap.min.css"/>
</head>
<body>
<div style="width: 500px; margin: 0 auto;">
<h1>编辑学生</h1>
<form class="form-horizontal" method="POST" action="/edit_student/{{ nid }}/" novalidate>
{% csrf_token %}
<div class="form-group">
<label class="col-sm-2 control-label">姓名:</label>
<div class="col-sm-10">
{{ obj.name }} {{ obj.name.errors.0 }}
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">邮箱:</label>
<div class="col-sm-10">
{{ obj.email }} {{ obj.email.errors.0 }}
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">年龄:</label>
<div class="col-sm-10">
{{ obj.age }} {{ obj.age.errors.0 }}
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">班级:</label>
<div class="col-sm-10">
{{ obj.cls_id }} {{ obj.cls_id.errors.0 }}
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-default">提交</button>
</div>
</div>
</form>
</div>
</body>
</html>
出现如下提示,是浏览器自带的验证功能,给 form
加上 novalidate
即可阻止:
常用插件
- checkBox
- radio
- iput
- textarea
- File
验证扩展
在原有验证规则基础上添加其他正则,会逐个进行验证:
from django.core.validators import RegexValidator
class MyForm(Form):
user = fields.CharField(
validators=[
RegexValidator(r'^[0-9]+$', '请输入数字'),
RegexValidator(r'^159[0-9]+$', '数字必须以159开头')
]
)
总结
- 使用
class Foo:
xx = xxxx() # 正则,插件
def clean_xx():
…
def clean():
pass
- 页面展示
obj = Foo()
# 灵活
<form>
{{ obj.xx }}
{{ obj.xx }}
…
<form>
# 简单
{{ obj.as_p }}
<ul>
{{ obj.as_ul }}
</ul>
<table>
{{ obj.as_table }}
</table>
- 后台
is_valid()
clean_data
errors
补充:文件上传
方式一:
import os
from django.core.files.uploadedfile import InMemoryUploadedFile
def f1(request):
if request.method == "GET":
return render(request, 'f1.html')
else:
file_obj = request.FILES.get('fafafa')
print(file_obj.name)
print(file_obj.size)
f = open(os.path.join('static', file_obj.name), 'wb')
for chunk in file_obj.chunks():
f.write(chunk)
f.close()
return render(request, 'f1.html')
f1.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form method="POST" action="/f1/" enctype="multipart/form-data">
{% csrf_token %}
<input type="file" name="fafafa">
<input type="submit" value="提交">
</form>
</body>
</html>
方式二:
class F2Form(Form):
user = fields.CharField()
fafafa = fields.FileField()
def f2(request):
if request.method == "GET":
obj = F2Form()
return render(request, 'f2.html', {'obj': obj})
else:
obj = F2Form(data=request.POST, files=request.FILES)
if obj.is_valid():
print(obj.cleaned_data.get('fafafa').name)
print(obj.cleaned_data.get('fafafa').size)
return render(request, 'f2.html', {'obj': obj})
f2.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form method="POST" action="/f2/" enctype="multipart/form-data">
{% csrf_token %}
{{ obj.user }}
{{ obj.fafafa }}
<input type="submit" value="提交">
</form>
</body>
</html>