django中的上传文件

用django写接口的时候,不可避免的会涉及到上传文件

环境

python版本 django版本 djangorestframework版本 drf-spectacular版本
3.10.4 3.2 3.14.0 0.27.1

编写模型

from django.db import models


class TimeMixin(models.Model):
    """
    时间混入类,为模型添加创建时间和更新时间字段。

    该类是一个抽象基类,提供了created_at和updated_at字段,这两个字段自动管理创建时间和更新时间。
    """

    class Meta:
        abstract = True  # 标记为抽象类,不会直接创建数据库表

    created_at = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')  # 自动设置创建时间
    updated_at = models.DateTimeField(auto_now=True, verbose_name='更新时间')  # 自动更新更新时间


class PlaybookRepository(TimeMixin, models.Model):
    """
    playbook仓库模型类,继承自TimeMixin,添加了playbook仓库相关字段。

    这个类描述了数据库中的playbook仓库表,包括仓库的名称、描述和包含的playbook文件。
    """

    class Meta:
        db_table = 'playbook_repository'  # 指定数据库表名
        verbose_name = 'playbook仓库表'  # 用于管理员界面的表名显示

    name = models.CharField(max_length=512, verbose_name='playbook仓库名称', unique=True)  # 库名称,唯一
    description = models.TextField(verbose_name='playbook仓库描述', null=True, blank=True)
    # 新建文件字段,用来上传的playbook文件,存储路径为playbooks下自动按年月日分类
    content = models.FileField(upload_to="playbooks/%Y/%m/%d/", verbose_name='playbook仓库文件',
                               null=False)

上面的模型创建了一个文件字段,并指定了存储路径

编写序列化器

from rest_framework import serializers
from .models import PlaybookRepository
from drf_spectacular.utils import extend_schema_field
from drf_spectacular.types import OpenApiTypes


# 定义一个用于文件上传的字段,因为需要上传,所以扩展了OpenAPI的二进制类型
@extend_schema_field(field=OpenApiTypes.BINARY)
class FileUploadField(serializers.FileField):
    pass

# 定义一个用于创建Playbook仓库的序列化器
class PlaybookRepositoryCreateSerializer(serializers.ModelSerializer):
    class Meta:
        # 指定序列化器使用的模型和字段
        model = PlaybookRepository
        fields = ["name", "description", "content"]

    # 使用FileUploadField作为content字段的类型,要求上传的内容为文件
    content = FileUploadField(required=True)

# 定义一个用于Playbook仓库的通用序列化器
class PlaybookRepositorySerializer(serializers.ModelSerializer):
    class Meta:
        # 指定序列化器使用的模型,并包含模型中的所有字段
        model = PlaybookRepository
        fields = "__all__"

上面包含了两个序列化器:

  • 一个用于创建Playbook仓库的序列化器(PlaybookRepositoryCreateSerializer),它特别包含了文件上传字段(content)
  • 另一个是用于Playbook仓库的通用序列化器(PlaybookRepositorySerializer),它包含了模型中的所有字段。

这里需要特别注意的是,需要重新定义content字段的类型,设置成OpenAPI的二进制类型

编写视图集

from rest_framework.viewsets import ReadOnlyModelViewSet
from rest_framework import mixins
from .models import PlaybookRepository
from .serializers import (
    PlaybookRepositorySerializer,
    PlaybookRepositoryCreateSerializer,
)
from drf_spectacular.utils import extend_schema
from .runner import Runner
from rest_framework.parsers import MultiPartParser, JSONParser, FormParser


# 定义一个视图集用于处理PlaybookRepository的视图操作
@extend_schema(tags=['PlaybookRepository'])
class PlaybookRepositoryViewSet(mixins.CreateModelMixin, ReadOnlyModelViewSet):
    """
    PlaybookRepository视图集,支持创建和只读操作。

    mixins.CreateModelMixin提供了创建模型实例的能力,
    ReadOnlyModelViewSet则提供了基于GET请求的只读操作。
    """
    queryset = PlaybookRepository.objects.all()  # 指定视图集查询的对象集合
    serializer_class = PlaybookRepositorySerializer  # 指定用于序列化和反序列化的序列化器
    parser_classes = (MultiPartParser, JSONParser, FormParser, )  # 指定支持的请求解析器类型

    @extend_schema(
            operation_id='CreatePlaybookRepository',
            summary='创建playbook仓库',
            # 指定请求参数,因为是上传文件,所以,这里要使用multipart/form-data类型
            request={"multipart/form-data": PlaybookRepositoryCreateSerializer},
            responses=PlaybookRepositorySerializer
    )
    def create(self, request, *args, **kwargs):
        """
        因为需要修改请求参数以及序列化器,所以这里需要重写一下,所以只是加了个装饰器,然后具体的处理逻辑还是交给父类
        """
        return super().create(request, *args, **kwargs)

这里需要注意的是,需要修改request的参数

settings设置

光有上面的模型、序列化器和视图集还不行

还需要配合全局设置

# settings.py
 
# 设置上传的文件根目录 我们上面模型中设置的 upload_to="playbooks/%Y/%m/%d/",那么上传的文件就会是在 BASE_DIR/'media'/'playbooks'/%Y/%m/%d/ 目录中
MEDIA_ROOT = BASE_DIR / 'media'

SPECTACULAR_SETTINGS = {
    # 设置这个参数,上传的文件就会是在项目目录下,而不是用户的家目录下
    'COMPONENT_SPLIT_REQUEST': True,
}

测试

此时打开swagger文档

就可以看到,已经可以正常上传文件了,并且会在swagger文档中出现上传的按钮,最后保存在数据库中的也是相对 MEDIA_ROOT = BASE_DIR / 'media' 这个路径的路径字符串

posted @ 2024-03-11 16:42  厚礼蝎  阅读(85)  评论(0编辑  收藏  举报