我有一个支持通过 Django 表单和 Django REST 框架上传文件的项目,这些文件用于存储在模型“文档”中。问题是,当通过 REST 框架上传验证失败的文件时,该文件仍保存到其“upload_to”参数 (MEDIA_ROOT)(未按预期创建“文档”模型实例),上传时不会发生这种情况
一些测试似乎表明“serializer.is_valid()”是保存文件的东西,尽管“serializer.is_valid()”在我所指的情况下是错误的。|| |我理想的结果是,当通过 REST 框架上传不可接受的文件时,该文件不存在于 MEDIA_ROOT 文件夹中。
如果您有任何建议,请留下答案/评论。
我的代码如下。| ||Models.py:
views.py:
Serializer.py:
from django.db import models
from apps.common.models import BaseModel
from datetime import datetime
import os
from django.core.exceptions import ValidationError
def file_storage_handler() -> str:
"""This function provides a file path to a Document model's FileField based on the current date. If the file path doesn't exist it creates it."""
currentDate = datetime.now().date()
path = f"uploaded_documents/{currentDate.year}/{currentDate.month}/{currentDate.day}/"
if not os.path.exists("media/"+path):
os.makedirs("media/"+path)
return path
def validate_file(file): # I dont believe that the specifics in this validator are important
fileType = file.name.split('.')[-1]
if not fileType in ['pdf','jpeg','jpg','png','bmp','tiff','heif']:
raise ValidationError(f"The .{fileType} file type is not supported")
class Document(BaseModel): # Note BaseModel is not a factor
file = models.FileField(upload_to=file_storage_handler(), validators=[validate_file])
document_type = models.ForeignKey(DocumentType, on_delete=models.PROTECT) # not important
forms.py:(问题并不重要)
# Document Endpoints
@api_view(["GET","POST"])
@parser_classes([MultiPartParser, FormParser])
def documents_general(request):
if request.method == "GET":
#some code, not important
elif request.method == "POST":
serializer = DocumentSerializer(data=request.data)
if serializer.is_valid(raise_exception=True): # I think that this line causes the file to save to the MEDIA_ROOT
documentObject = serializer.save()
on_upload.delay(documentObject.id)
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
如果您有建议或问题,请告诉我。
class DocumentSerializer(serializers.ModelSerializer):
class Meta:
model = Document
fields = '__all__'
def update(self, instance, validated_data): # TBH IDK why this is here, likely not important
"""This method is called when the serializer is saved, here I am updating the fields which should be updated."""
instance.document_type = validated_data.get('document_type', instance.document_type)
instance.save()
return instance
forms.py: (prob not important)
class DocumentForm(forms.ModelForm):
class Meta:
model = Document
fields = ["file", "document_type"]
Please let me know if you have advice or questions.
The problem is that Django's
FileField
saves uploaded files to the disk
as soon as they are accessed
, which happens during the validation phase. This means that even if
is_valid()
returns
False
, the file has already been saved.
Here's how to fix it:
1. Use
request.FILES
directly in your view:
Instead of relying on the serializer to handle the file, access it directly from
request.FILES
and perform validation manually.
@api_view(["GET", "POST"])
@parser_classes([MultiPartParser, FormParser])
def documents_general(request):
if request.method == "POST":
uploaded_file = request.FILES.get('file') # Get file from request
# Manual validation
try:
validate_file(uploaded_file)
except ValidationError as e:
return Response({'error': str(e)}, status=status.HTTP_400_BAD_REQUEST)
# If validation succeeds, create the Document object
if uploaded_file:
documentObject = Document.objects.create(
file=uploaded_file,
document_type=request.data.get('document_type')
)
on_upload.delay(documentObject.id)
return Response({'message': 'Document uploaded successfully'}, status=status.HTTP_201_CREATED)
# ... rest of your view
2. Modify the serializer to handle file saving explicitly:
You can customize the serializer's
create()
method to control file saving:
class DocumentSerializer(serializers.ModelSerializer):
# ... your existing code ...
def create(self, validated_data):
# Access file directly from validated_data
uploaded_file = validated_data.pop('file')
# Create document object without saving the file yet
documentObject = Document.objects.create(**validated_data)
# Save the file manually
documentObject.file.save(uploaded_file.name, uploaded_file)
documentObject.save()
return documentObject
3. Use a temporary file storage:
This approach involves configuring Django to use a temporary file storage during the validation phase. If validation fails, the file is discarded. If it succeeds, the file is moved to the permanent storage.
You can find more information on configuring temporary file storage in the Django documentation: https://docs.djangoproject.com/en/4.2/ref/files/storage/#django.core.files.storage.FileSystemStorage.location
These are some solutions to prevent files from being saved when validation fails in your Django REST Framework application. Choose the one that best suits your needs and project structure.
标签:python,django,django-models,django-rest-framework,django-storage From: 78786007