文章目录跟b站武沛齐老师的视频学习
对学习过程进行一些记录以便复习 同时自我督促 :D冲鸭视频链接: 15天django入门到放弃-哔哩哔哩.
- Form 验证整理
- ajax提交 例
- 相关参数(生成HTML标签)
- 保留上次输入内容
- 示例
- 班级管理
- 学生管理
- 教师管理
- 数据无法动态显示
- 插件及样式定制
- 常用插件
- 验证扩展
- 总结
- 补充:文件上传
本篇均为 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):
字段 = 正则表达式
字段 = 自定义正则表达式
- 保留上次输入内容
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();;
代码如下:
Title
用户登录
相关参数(生成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:
保留上次输入内容
- 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('1618d+')
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:
Title
班级列表
-
{% for row in cls_list %}
- {{ row.title }} 编辑 {% endfor %}
add_list.html:
Title
添加班级
注意,编辑班级对应的 url 为:re_path(r'^edit_class/(d+)/', views.edit_class),;
edit_class.html:
Title
编辑班级
学生管理
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:
Title
学生列表
添加
-
{% for row in stu_list %}
- {{ row.name }}-{{ row.email }}-{{ row.age }}-{{ row.cls_id }}-{{ row.cls.title }} 编辑 {% endfor %}
add_student.html:
Title
添加学生
edit_student.html:
Title
编辑学生
教师管理
多选:
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')
得到数据格式为:
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:
Title
教师列表
| ID | 姓名 | 任教班级 | 操作 |
|---|---|---|---|
| {{ row.id }} | {{ row.tname }} | {% for item in row.c2t.values %} {{ item.title }} {% endfor %} | 编辑 |
add_teacher.html;
Title
edit_teacher.html:
Title
编辑老师
数据无法动态显示
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:
Title
编辑学生
出现如下提示,是浏览器自带的验证功能,给 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()
# 灵活
- 后台
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:
Title
方式二:
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:
Title



