用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 Job, 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, status
from .models import Job, PlaybookRepository
from .serializers import (
JobSerializer, JobCreateSerializer, 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'
这个路径的路径字符串