XH_Digital_Management/common/views.py

182 lines
6.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import json
import os
import urllib.parse
import openpyxl
from openpyxl.utils import get_column_letter
from openpyxl.worksheet.datavalidation import DataValidation
from django.apps import apps
from django.core.exceptions import ValidationError
from openpyxl import load_workbook
from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.contrib.staticfiles import finders
from django.core.files.storage import default_storage
from django.http import FileResponse, HttpResponseNotFound, JsonResponse
from django.views.decorators.csrf import csrf_exempt, csrf_protect
from rest_framework.decorators import api_view
from rest_framework.serializers import ModelSerializer
@login_required
def download_excel_template(request, template_name, fields):
"""
该视图提供下载指定的Excel模板文件。
参数:
request: HttpRequest对象。
template_name: 请求下载的Excel模板文件的名称期望是一个字符串。
fields: 包含字段信息的字典用于生成Excel文件的表头和格式。
返回:
FileResponse对象允许用户下载生成的Excel文件。
如果文件未找到则返回一个HttpResponseNotFound响应。
"""
fields = json.loads(urllib.parse.unquote(fields).replace("'", '"'))
# 创建一个新的工作簿
wb = openpyxl.Workbook()
ws = wb.active
# 设置表头行
headers = [config['label'] for field, config in fields.items()]
ws.append(headers)
# 保存生成的Excel文件到指定路径
static_dir = os.path.join(settings.BASE_DIR, 'static', 'excels')
if not os.path.exists(static_dir):
os.makedirs(static_dir)
temp_file_path = os.path.join(static_dir, template_name)
wb.save(temp_file_path)
# 创建FileResponse对象设置为以附件形式下载设置MIME类型为Excel文件
response = FileResponse(open(temp_file_path, 'rb'),
content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
response['Content-Disposition'] = f'attachment; filename="{urllib.parse.quote(template_name)}"'
return response
@csrf_protect
@login_required
def common_excel_parse(request):
"""
该函数用于解析上传的Excel文件并返回数据。
Args:
request: HTTP请求对象包含上传的Excel文件。
Returns:
JsonResponse: 包含解析后的Excel数据的JSON响应或包含错误消息的JSON响应。
Raises:
N/A
"""
# 如果是POST请求并且有上传的文件
if request.method == 'POST' and request.FILES:
# 获取上传的Excel文件
excel_file = request.FILES.get('excel_file')
# 如果没有提供文件,返回错误响应
if not excel_file:
return JsonResponse({'error': '请先选择文件。'}, status=400)
# 获取fields_map和model_config
fields_map = json.loads(request.POST.get('fields_map', '{}'))
model_config = json.loads(request.POST.get('model_config', '{}'))
# 动态获取模型
try:
model = apps.get_model(model_config['app_label'], model_config['model_name'])
except (LookupError, KeyError):
return JsonResponse({'error': '模型配置错误。'}, status=400)
columns = list(fields_map.values())
fields = list(fields_map.keys())
# 保存文件到服务器
file_name = default_storage.save(excel_file.name, excel_file)
file_path = os.path.join(settings.MEDIA_ROOT, file_name)
def create_dynamic_serializer(mod):
class DynamicSerializer(ModelSerializer):
class Meta:
model = mod
fields = '__all__'
return DynamicSerializer
try:
# 打开并解析Excel文件
workbook = load_workbook(file_path, data_only=True)
sheet = workbook.active
data = []
# 读取第一行作为表头
header_row = [cell.value for cell in sheet[1]]
# 检查表头是否与fields_map的values对应
if header_row != columns:
return JsonResponse({'error': '表头不匹配请使用正确的Excel上传模板。'}, status=400)
for row in sheet.iter_rows(min_row=2, values_only=True):
if not all(value is None for value in row):
instance_data = dict(zip(fields, row))
instance = model(**instance_data)
try:
instance.full_clean()
data.append(instance)
except ValidationError as e:
return JsonResponse({'error': f'数据校验错误: {e.message_dict}'}, status=400)
# 动态获取序列化器
serializer_class = create_dynamic_serializer(model)
serializer = serializer_class(data, many=True)
# 清理,删除上传的文件
os.remove(file_path)
return JsonResponse(serializer.data, safe=False)
except Exception as e:
# 清理,删除上传的文件
os.remove(file_path)
return JsonResponse({'error': f'解析文件时出错: {str(e)}'}, status=500)
return JsonResponse({'error': '请求错误'}, status=400)
@csrf_protect
@login_required
def save_excel_table_data(request):
if request.method == 'POST':
try:
data = json.loads(request.body)
app_label = data.get('app_label')
model_name = data.get('model_name')
table_data = data.get('table_data')
if not app_label or not model_name or not table_data:
return JsonResponse({'error': '缺少必要的参数'}, status=400)
Model = apps.get_model(app_label, model_name)
for row_data in table_data:
try:
instance = Model(**row_data)
instance.full_clean() # 验证数据
instance.save()
except ValidationError as e:
return JsonResponse({'error': f'数据校验错误: {e.message_dict}'}, status=400)
except Exception as e:
return JsonResponse({'error': f'保存数据时出错: {str(e)}'}, status=500)
return JsonResponse({'message': '表格数据保存成功'}, status=200)
except json.JSONDecodeError:
return JsonResponse({'error': '无效的JSON格式'}, status=400)
except Exception as e:
return JsonResponse({'error': f'服务器内部错误: {str(e)}'}, status=500)
return JsonResponse({'error': '无效的请求方法'}, status=400)