from pathlib import Path from django import forms from apps.scenarios.services import ScenarioNotFound, get_scenario from apps.scenarios.services import list_scenarios SUPPORTED_EXTENSIONS = {".txt", ".md", ".pdf", ".docx", ".zip", ".7z", ".rar"} class MultipleFileInput(forms.ClearableFileInput): allow_multiple_selected = True class MultipleFileField(forms.FileField): widget = MultipleFileInput def clean(self, data, initial=None): single_file_clean = super().clean if not data: return [] if isinstance(data, (list, tuple)): return [single_file_clean(item, initial) for item in data] return [single_file_clean(data, initial)] class DocumentUploadForm(forms.Form): # 使用 ChoiceField 让表单自己维护场景选项, # 这样模板、校验和后续扩展都能围绕一个入口完成。 scenario_id = forms.ChoiceField(label="场景", choices=()) files = MultipleFileField(label="文件或资料包", required=False) file = forms.FileField(label="兼容单文件上传", required=False) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields["scenario_id"].choices = [ (scenario["id"], scenario["name"]) for scenario in list_scenarios() ] def clean_scenario_id(self): scenario_id = self.cleaned_data["scenario_id"] try: get_scenario(scenario_id) except ScenarioNotFound as exc: raise forms.ValidationError("场景不存在") from exc return scenario_id def clean_file(self): uploaded_file = self.cleaned_data["file"] if not uploaded_file: return uploaded_file extension = Path(uploaded_file.name).suffix.lower() if extension not in SUPPORTED_EXTENSIONS: raise forms.ValidationError("仅支持 .txt、.md、.pdf、.docx、.zip、.7z 和 .rar 文件") return uploaded_file def clean_files(self): uploaded_files = self.cleaned_data.get("files") or [] for uploaded_file in uploaded_files: extension = Path(uploaded_file.name).suffix.lower() if extension not in SUPPORTED_EXTENSIONS: raise forms.ValidationError("仅支持 .txt、.md、.pdf、.docx、.zip、.7z 和 .rar 文件") return uploaded_files def clean(self): cleaned_data = super().clean() files = list(cleaned_data.get("files") or []) file = cleaned_data.get("file") if file: files.append(file) if not files: raise forms.ValidationError("请至少上传一个文件或一个 zip 资料包。") cleaned_data["uploaded_files"] = files return cleaned_data