野外泛在线考核系统(七)
二、笔记本端
接(六),主要实现数据管理
(五)数据管理
在这3天都在折腾利用form.py实现数据管理,特别是文件上传,多个文件上传的事情,之前文件上传其实已经没有问题,但问题是不是用form.py实现的,这里主要是想通过form.py实现,因此又折腾了这么久。主要参考《Django3 Web应用开发实践》(黄永祥著)这本书8.9多文件上传的例子。遇到了很多的问题,今天好好梳理下
1.数据重新迁移
在最开始设计数据表的时候,可能不是很完善,到后来又想增加几个字段,或者各表再建立相关联系等等,对于初学者很常见,因此,就会涉及到重新进行迁移,但这里需要注意几个问题
【注意】
1.迁移后在mysql中所有表名都是小写的,意味着如果在models.py中字母相同,但大小写不同的两个表,在mysql中是一个表,会冲突
2.models.py的各个表中如果有相同名称的字段,迁移时也可能会发生冲突,比如student表中有个ID字段(不是默认的,是自定义的),teancher表中也有个ID字段(不是默认的,是自定义的)那么迁移时就会报错,为了不冲突,建议分别改成studentID和teacherID。
如果迁移途中发生错误,希望从头再来,可按照如下步骤实施:
- 打开终端(我是麒麟系统)
- 登录mysql
tactics@qbpb:~$ mysql -u root -p
- 直接删除数据库
mysql> drop database artillery
- 重新创建数据库
mysql> CREATE DATABASE qbpb
- 打开pycharm,删除Lesson/migrations下除__int__.py以外的所有文件。
- 重新设置setting.py数据库设置,因为数据库改名了(不改名可略过),而后执行数据迁移:
python manage.py makemigrations
python manage.py migrate
采取这样的方法,也省去了折腾,但如果有数据那就不好办了,可能得另寻别的方法。
2.通过form.py实现信息录入和多文件上传
- 在Lesson/form.py中,加入如下内容,这里主要参考了Django官方文件上传
class MultipleFileInput(forms.ClearableFileInput):
allow_multiple_selected = True
class MultipleFileField(forms.FileField):
def __init__(self, *args, **kwargs):
kwargs.setdefault("widget", MultipleFileInput())
super().__init__(*args, **kwargs)
def clean(self, data, initial=None):
single_file_clean = super().clean
if isinstance(data, (list, tuple)):
result = [single_file_clean(d, initial) for d in data]
else:
result = single_file_clean(data, initial)
return result
class StudentForm(forms.ModelForm):
# allow_empty_file=True 是允许上传空文件
#studentPhoto = forms.FileField(label='照片', allow_empty_file=True,widget=forms.ClearableFileInput(attrs={'multiple': False}))
studentPhoto = MultipleFileField(label='照片', allow_empty_file=True)
class Meta:
model = Student
fields = '__all__'
labels = {
'studentID': '编号',
'studentName': '姓名',
'studentGender': '性别',
'studentIDPhoto': '照片'
}
error_messages = {
'__all__': {'required': '请输入内容',
'invalid': '请检查输入内容'},
}
- 《Django3 Web应用开发实践》(黄永祥著)这本书8.9多文件上传的例子中是这样的,但会报错ValueError: ClearableFileInput doesn’t support uploading multiple files
studentPhoto = forms.FileField(label='照片', allow_empty_file=True,widget=forms.ClearableFileInput(attrs={'multiple': False}))
- 为了解决错误,需要改成这样才行。
studentPhoto = MultipleFileField(label='照片', allow_empty_file=True)
- 在Lesson下新建adminViews.py
# adminViews.py
from django.shortcuts import render
from django.http import HttpResponse
from .form import *
from .models import *
from django.conf import settings
import os
def adminData(request):
# GET请求
if request.method == 'GET':
id = request.GET.get('id', '')
if id:
i = Student.objects.filter(id=id).first()
p = StudentForm(instance=i)
else:
p = StudentForm()
return render(request, 'manage.html', locals())
# POST请求
else:
p = StudentForm(data=request.POST, files=request.FILES)
if p.is_valid():
name = p.cleaned_data['studentName']
result = Student.objects.filter(studentName=name)
# 数据不存在,则新增数据
if not result:
p = p.save()
id = p.id
# 遍历上存的文件,依次添加证件信息
# 如果网页有多个文件上存控件
# 可以通过getlist方法获取指定的文件上存控件
for f in request.FILES.getlist('studentPhoto'):
# 修改文件名
# 如果不同用户上存相同文件名的文件
# 防止media文件夹会覆盖文件
f.name = f'{id}.'.join(f.name.split('.'))
d = dict(student_id=id, studentPhoto=f)
StudentPhoto.objects.create(**d)
return HttpResponse('新增成功')
# 数据存在,则修改数据
else:
studentID = p.cleaned_data['studentID']
d = dict(studentName=name, studentID=studentID)
result.update(**d)
# 删除旧的证件
id = result.first().id
# 删除media文件夹的文件,然后删除数据表的数据
for c in StudentPhoto.objects.filter(student_id=id):
# c.studentPhoto是文件对象,通过name属性获取文件名
# 删除media文件夹的文件
fn = c.studentPhoto.name
os.remove(os.path.join(settings.MEDIA_ROOT, fn))
# 删除数据表的数据
c.delete()
# 添加新的证件
for f in request.FILES.getlist('studentPhoto'):
# 修改文件名
# 如果不同用户上存相同文件名的文件
# 防止media文件夹会覆盖文件
f.name = f'{id}.'.join(f.name.split('.'))
d = dict(student_id=id, studentPhoto=f)
StudentPhoto.objects.create(**d)
return HttpResponse('修改成功')
else:
# 获取错误信息,并以json格式输出
error_msg = p.errors.as_json()
print(error_msg)
return render(request, 'manage.html', locals())
- 而后修改Lesson/urls.py,
# urls.py
from django.urls import path
from . import views
from .views import *
from .adminViews import *
urlpatterns = [
path('', views.index,name='index'),
# 管理数据
path('adminData', adminData, name='adminData'),
]
- 然后新建一个manage.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" enctype="multipart/form-data">
{% csrf_token %}
<ul>
<li>编号:{{ p.studentID }}</li>
<li>姓名:{{ p.studentName }}</li>
<li>性别:{{ p.studentGender }}</li>
<li>证件:{{ p.studentIDPhoto }}</li>
<li>照片:{{ p.studentPhoto }}</li>
</ul>
<input type="submit" value="提交">
</form>
{% endif %}
</body>
</html>
在index.html中加入链接:
<a href="adminData"><h3 >管理数据</h3></a>
至此,文件下载,上传功能完工,最终效果。