首页 > 编程问答 >Django REST框架serializer.is_valid()将文件保存到MEDIA_ROOT,尽管.is_valid()为False

Django REST框架serializer.is_valid()将文件保存到MEDIA_ROOT,尽管.is_valid()为False

时间:2024-07-24 12:12:49浏览次数:14  
标签:python django django-models django-rest-framework django-storage

我有一个支持通过 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

相关文章

  • 如何在Python的matplotlib中将条形标签绘制到右侧并为条形标签添加标题?
    我已经在python中的matplotlib中创建了一个图表,但是以下代码中的最后一行不允许在图表之外对齐条形标签。importmatplotlib.pyplotaspltg=df.plot.barh(x=name,y=days)g.set_title("Dayspeopleshowedup")g.bar_label(g.containers[0],label_type='edge')我得......
  • 19、Python之容器:快来数一数,24678?Counter能数得更好
    引言关于数据的分组计数,前面的文章中已经涉及了很多次。眼下要进行分组计数,我们可用的方法有:1、直接使用dict进行计数,需要对首次出现的键进行判断初始化的操作;2、使用dict的setdefault()方法进行计数,代码可以简化一些,虽然方法名有点怪;3、defaultdict进行计数,可以设置自动......
  • 如何使用 C# 检查用户是否安装了最低 Python 版本并且可以访问我的代码?
    我正在开发一个C#程序,该程序必须为一项特定任务运行一些Python代码。(Python代码很复杂,是由另一个团队开发的。无法在C#中重现其功能。)我正在尝试更新我的程序的安装程序文件以解决此问题:我希望它检查用户是否(谁正在安装我的程序)已安装Python并且它满足我的最低版......
  • 如何优雅地将复杂的Python对象和SQLAlchemy对象模型类结合起来?
    我有一个相当复杂的类,具有从提供的df到init计算的复杂属性,这些属性可能是最终可以序列化为字符串的其他类类型。在Python中,我想处理对象而不是原始类型,但也想使用SQLAlchemy与数据库交互。表中的列与许多类属性相同,如何优雅地组合这两个类?我可以使用组合并将数据......
  • Python Match Case:检查未知长度的可迭代内部的类型
    我想使用匹配大小写检查一个未知长度的迭代(假设为list)仅包含给定类型(假设为float)(还有其他情况,只有这个给我带来了问题)。case[*elems]ifall([isinstance(elem,float)foreleminelems]):returnnum这个似乎可行,但确实很不Pythony。看来应该有更简单的方法。......
  • Python实现excel数据的读取和写入
    1.安装说到前面的话,实现excel文件数据的读取和写入,在python中还有其它方法,比如说pandas。鉴于最近粉丝朋友问到上面的“xlrd”和“xlwt”,那么笔者下面将通过这两个方法,来实现excel文件数据的读取和写入。首先,我们先需要提前安装好对应的库。需要注意的是,xlrd从2.0版本开始,只......
  • python_进程与线程_多线程
    一、程序与进程的概念1、进程:指启动后的程序,系统会为进程分配内存空间二、创建进程的方式1、第一种创建进程的方式process(group=None,target,name,args,kwargs)group:表示分组,实际上不使用,默认为None即可target:表示子进程要执行的任务,支持函数名name:表示子进程的......
  • python_网络编程_socket
    一、网络编程的基本概念通信协议:internet协议,任何私有网络支持此协议,就可以接入互联网二、七层协议与四层协议从下到上分别是:物理层、数据链路层、网络层、传输层、会话层、表示层、应用层三、掌握TCP、IP协议ip协议是整个TCP、IP协议族的核心IP地址就是会联网上计算......
  • 你能对 Python 类型注释进行模式匹配吗?
    你能在Python类型上进行模式匹配吗?我见过简单的例子:importbuiltinsmatchx:casebuiltins.str:print("matchedstr")casebuildins.int:print("matchedint")但我想在嵌套类型上进行模式匹配,比如Annotated[Optional[Literal["a",......
  • python Polars:替换嵌套列表的元素
    有谁知道是否可以有效地替换极坐标中嵌套列表的元素。例如:s=pl.Series('s',[[1,2,3],[3,4,5]])#replace3with10toget[[1,2,10],[10,4,5]]我已经尝试过s.to_frame().with_columns(pl.when(pl.col('s')==3)...)但是pl.when不喜欢List[bo......