第八章——表单和模型
完整的表单主要由4个部分组成:
- 提交地址:即form标签的action属性
- 请求方式:form标签的method属性
- 元素控件: 控件
- 提交按钮:控件
# forms.py
from django import forms
from django.core.exceptions import ValidationError
from .models import PersonInfoed, Vocation
# 自定义数据验证函数
def payment_validate(value):
if value > 30000:
raise ValidationError('请输入合理的薪资')
class VocationForm(forms.Form):
job = forms.CharField(max_length=20, label='职位')
title = forms.CharField(max_length=20, label='职称',
widget=forms.widgets.TextInput(attrs={'class': 'c1'}),
error_messages={'required': '职称不能为空'})
payment = forms.IntegerField(label='薪资',validators=[payment_validate]) # 数据验证
values= PersonInfoed.objects.values('name')
choices = [(i+1, v['name']) for i, v in enumerate(values)]
person = forms.ChoiceField(choices=choices, label='姓名')
# 数据清洗 clean_ + 字段名
def clean_title(self):
data = self.cleaned_data['title']
return '初级' + data
ModelForm
class VocationForm(forms.ModelForm):
LEVEL = (('L1', '初级'),
('L2', '中级'),
('L3', '高级'))
level = forms.ChoiceField(choices=LEVEL, label='级别')
class Meta:
model = Vocation
fields = ['job', 'title', 'payment', 'person']
# fields = '__all__'
# exclude = []
labels = {
'job': '职位',
'title': '职称',
'payment': '薪资',
'person': '姓名'
}
widgets = {
'job': forms.widgets.TextInput(attrs={'class': 'c1'}),
}
# 重新定义字段类型
field_classes = {
'job': forms.CharField
}
# 帮助信息
help_texts = {
'job': '请输入职位名称'
}
# 自定义错位信息
error_messages = {
# __all__ 设置全部错误信息
'__all__': {
'required': '请输入内容',
'invalid': '请检查输入内容',
# 设置某个字段的错误信息
'title': {
'required': '请输入职称内容',
'invalid': '请检查职称输入内容',
}
}
}
def clean_payment(self):
'''自定义表单字段清洗'''
data = self.cleaned_data['payment'] + 100
return data
视图里使用Form
forms.py
class VocationForm(forms.Form):
job = forms.CharField(max_length=20, label='职位')
title = forms.CharField(max_length=20, label='职称',
widget=forms.widgets.TextInput(attrs={'class': 'c1'}),
error_messages={'required': '职称不能为空'})
payment = forms.IntegerField(label='薪资',validators=[payment_validate])
values= PersonInfoed.objects.values('name')
choices = [(i+1, v['name']) for i, v in enumerate(values)]
person = forms.ChoiceField(choices=choices, label='姓名')
def clean_title(self):
data = self.cleaned_data['title']
return '初级' + data
views.py
def index(request):
if request.method == 'GET':
id = request.GET.get('id', '')
if id:
d = Vocation.objects.filter(id=id).values()
print('ddd', d)
d = list(d)[0]
d['person'] = d['person_id']
i = dict(initial=d, label_suffix='*', prefix='vv')
v = VocationForm(**i)
else:
v = VocationForm(prefix='vv')
return render(request, 'index.html', locals())
else:
v = VocationForm(data=request.POST, prefix='vv')
print('request_data', request.POST)
# print('vv', v)
if v.is_valid():
title = v['title']
# print('title1', title)
ctitle = v.cleaned_data['title']
# print('ctitle', ctitle)
id = request.GET.get('id', '')
d = v.cleaned_data
print('d1d1d1', d)
d['person_id'] = int(d['person'])
Vocation.objects.filter(id=id).update(**d)
return HttpResponse('提交成功')
else:
error_msg = v.errors.as_json()
print('error_msg', error_msg)
return render(request, 'index.html', locals())
视图里使用ModelForm
forms.py
class VocationForm(forms.ModelForm):
# LEVEL = (('L1', '初级'),
# ('L2', '中级'),
# ('L3', '高级'))
# level = forms.ChoiceField(choices=LEVEL, label='级别')
class Meta:
model = Vocation
fields = ['job', 'title', 'payment', 'person']
# fields = '__all__'
# exclude = []
labels = {
'job': '职位',
'title': '职称',
'payment': '薪资',
'person': '姓名'
}
# widgets = {
# 'job': forms.widgets.TextInput(attrs={'class': 'c1'}),
# }
# # 重新定义字段类型
# field_classes = {
# 'job': forms.CharField
# }
# 帮助信息
# help_texts = {
# 'job': '请输入职位名称'
# }
# 自定义错位信息
error_messages = {
# __all__ 设置全部错误信息
'__all__': {
'required': '请输入内容',
'invalid': '请检查输入内容',
# 设置某个字段的错误信息
'title': {
'required': '请输入职称内容',
'invalid': '请检查职称输入内容',
}
}
}
def clean_payment(self):
'''自定义表单字段清洗'''
data = self.cleaned_data['payment'] + 100
return data
views.py
def index(request):
if request.method == 'GET':
id = request.GET.get('id', '')
if id:
d = Vocation.objects.filter(id=id).first()
print('dd', d)
v = VocationForm(instance=d, prefix='vv')
else:
v = VocationForm(prefix='vv')
return render(request, 'index.html', locals())
else:
v = VocationForm(data=request.POST, prefix='vv')
print('request_POST', request.POST)
print('request_GET', request.GET)
# print('vv', v)
if v.is_valid():
id = request.GET.get('id', '')
result = Vocation.objects.filter(id=id)
if not result:
# method one
# v.save()
# method two
v1 = v.save(commit=False)
v1.title = '初级' + v1.title
v1.save()
# method three
# v.save_m2m() # 保存manytomany的数据模型
return HttpResponse('新增成功')
else:
d = v.cleaned_data
d['title'] = '中级' + d['title']
result.update(**d)
return HttpResponse('修改成功')
else:
error_msg = v.errors.as_json()
print('error_msg', error_msg)
return render(request, 'index.html', locals())
同一个网页多个表单
views.py
def index(request):
if request.method == 'GET':
v = VocationForm(prefix='vv')
w = VocationForm(prefix='ww')
return render(request, 'index.html', locals())
else:
v = VocationForm(data=request.POST, prefix='vv')
w = VocationForm(data=request.POST, prefix='ww')
print('request_POST', request.POST)
print('request_GET', request.GET)
# print('vv', v)
if v.is_valid():
HttpResponse('表单一提交成功')
elif w.is_valid():
HttpResponse('表单二提交成功')
else:
error_msg = v.errors.as_json()
print('error_msg', error_msg)
return render(request, 'index.html', locals())
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{% if v.errors %}
<p>
数据出错啦,错误信息:{{ v.errors }}
</p>
{% else %}
<form action="" method="post">
{% csrf_token %}
<table>
{{ v.as_table }}
</table>
<input type="submit" value="提交">
</form>
<form action="" method="post">
{% csrf_token %}
<table>
{{ w.as_table }}
</table>
<input type="submit" value="提交">
</form>
{% endif %}
</body>
</html>
一个表单多个按钮
views.py
def index(request):
if request.method == 'GET':
v = VocationForm(prefix='vv')
return render(request, 'index.html', locals())
else:
v = VocationForm(data=request.POST, prefix='vv')
if v.is_valid():
if 'add' in request.POST:
return HttpResponse('提交成功')
else:
return HttpResponse('修改成功')
else:
error_msg = v.errors.as_json()
print('error_msg', error_msg)
return render(request, 'index.html', locals())
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{% if v.errors %}
<p>
数据出错啦,错误信息:{{ v.errors }}
</p>
{% else %}
<form action="" method="post">
{% csrf_token %}
<table>
{{ v.as_table }}
</table>
<input type="submit" name="add" value="提交">
<input type="submit" name="update" value="修改">
</form>
{% endif %}
</body>
</html>
表单的批量处理
views.py
from django.shortcuts import render
from django.http import HttpResponse
from django import forms
from .forms import VocationForm
from .models import Vocation
# Create your views here.
def index(request):
# 参数extra 设置表单数量
# max_num 限制表单的最大数量
pfs = forms.formset_factory(VocationForm, extra=2, max_num=5)
# 另一种
# pfs = forms.modelformset_factory(model=Vocation, form=VocationForm, extra=0, max_num=5)
if request.method == 'GET':
p = pfs()
return render(request, 'index.html', locals())
else:
p = pfs(request.POST)
if p.is_valid():
for i in p:
i.save()
return HttpResponse('新增成功')
else:
error_msg = p.errors.as_json()
print('error_msg', error_msg)
return render(request, 'index.html', locals())
多文件批量上传
# models.py
# 设置模型,配置上传地址
class Mystorage(FileSystemStorage): # 对上传文件做自定义处理
def get_available_name(self, name, max_length=None):
print('name', name)
if self.exists(name):
print('jjj')
os.remove(os.path.join(settings.MEDIA_ROOT, name))
return name
class CertificateInfo(models.Model):
id = models.AutoField(primary_key=True)
certificate = models.FileField(blank=True, upload_to='images/', storage=Mystorage()) # 文件上传
person = models.ForeignKey(PersonInfoed, on_delete=models.CASCADE, blank=True, null=True)
def __str__(self):
return str(self.person)
class Meta:
verbose_name = '证件信息'
# forms.py
class PersonInfoForm(forms.ModelForm):
certificate = forms.FileField(label='证件', allow_empty_file=True,
widget=forms.ClearableFileInput(attrs={'multiple': True})) # 多文件上传
class Meta:
model = PersonInfoed
fields = '__all__'
labels = {
'name': '姓名',
'age': '年龄'
}
# views.py
# 视图中处理
def index(request):
if request.method == 'GET':
id = request.GET.get('id', '')
if id:
i = PersonInfoed.objects.filter(id=id).first()
p = PersonInfoForm(instance=i)
else:
p = PersonInfoForm()
return render(request, 'index.html', locals())
else:
p = PersonInfoForm(data=request.POST, files=request.FILES)
if p.is_valid():
name = p.cleaned_data['name']
result = PersonInfoed.objects.filter(name=name)
if not result:
p.save()
id = p.id
print('here1', request.FILES.getlist('certificate'))
for f in request.FILES.getlist('certificate'):
f.name = f"{id}.".join(f.name.split('.'))
d = dict(person_id=id, certificate=f)
CertificateInfo.objects.create(**d)
return HttpResponse('新增成功')
else:
age = p.cleaned_data['age']
d = dict(name=name, age=age)
result.update(**d)
id = result.first().id
print('id', id)
for c in CertificateInfo.objects.filter(person_id=id):
fn = c.certificate.name
os.remove(os.path.join(settings.MEDIA_ROOT, fn))
c.delete()
print('here2', request.FILES.getlist('certificate'))
for f in request.FILES.getlist('certificate'):
print('here3', f.name, type(f.name))
f.name = f"{id}.".join(f.name.split('.'))
print('hh', f.name)
d = dict(person_id=id, certificate=f)
CertificateInfo.objects.create(**d)
return HttpResponse('修改成功')
else:
error_msg = p.errors.as_json()
print('error_msg', error_msg)
return render(request, 'index.html', locals())
项目路由 urls.py
from django.contrib import admin
from django.urls import path, include, re_path
from django.views.static import serve
from django.conf import settings
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('app1.urls')),
]
urlpatterns += [re_path("media/(?P<path>.*)", serve, {"document_root": settings.MEDIA_ROOT}, name='media')]
# settings.py 配置
MEDIA_URL = "/media/" # 媒体路由地址
MEDIA_ROOT = BASE_DIR / "media" # 媒体路径信息
# html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{% if p.errors %}
<p>
数据出错啦,错误信息:{{ p.errors }}
</p>
{% else %}
<form action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
<ul>
<li>姓名:{{ p.name }}</li>
<li>年龄:{{ p.age }}</li>
<li>证件:{{ p.certificate }}</li>
</ul>
<input type="submit" value="提交">
</form>
{% endif %}
</body>
</html>