Django初窥门径-自定义附件存储模型

前言

Django自带了一个名为FileField的字段,用于处理文件上传。然而,有时我们需要更多的控制权,例如定义文件的存储路径、文件名以及文件类型。在本篇文章中,我们将探讨如何自定义Django附件存储模型。

创建attachment应用

bash 复制代码
python manage.py startapp attachment

然后,在项目的settings.py文件中,将应用注册到INSTALLED_APPS列表中,如下所示:

py 复制代码
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',
    'drf_yasg2',
    'django_filters',
    'account.apps.AccountConfig',
    'oauth',
    'attachment'
]

创建模型

定义Attachment

py 复制代码
from django.db import models

# Create your models here.
from rest_framework.reverse import reverse

from CodeVoyager.mixins import BaseModelMixin
import uuid


class BlobField(models.Field):
    description = 'Blob'

    def db_type(self, connection):
        return 'mediumblob'


class Attachment(BaseModelMixin):
    file_id = models.UUIDField(auto_created=True, default=uuid.uuid4, editable=False)
    file_name = models.CharField('文件名', max_length=200, unique=True)
    mime_type = models.CharField('MIME类型', max_length=100)
    file_size = models.PositiveIntegerField('文件长度')
    blob = BlobField('文件内容')

    class Meta:
        verbose_name = '附件'
        verbose_name_plural = verbose_name

    def get_url(self, request):
        return reverse('attachment:download', request=request, kwargs={'attachment_id': self.file_id})
字段名称 类型 用途
file_id UUIDField 存储文件的唯一标识符
file_name CharField 存储文件的名称,即原始文件名
mime_type CharField 存储文件的MIME类型
file_size PositiveIntegerField 存储文件的大小(以字节为单位)
blob 自定义 BlobField 存储文件的二进制内容,即文件的实际数据

将更改应用到数据库

bash 复制代码
python manage.py makemigrations
python manage.py migrate

自定义Django存储

定义存储类

py 复制代码
#!/usr/bin/python
# -*- coding: utf-8 -*-

from django.core.files.base import ContentFile, File
from django.core.files.storage import Storage
from django.utils.deconstruct import deconstructible


@deconstructible
class AttachmentStorage(Storage):
    """附件存储"""

    def __init__(self, model=None):
        from .models import Attachment
        self.model = Attachment

    def _open(self, file_id, mode='rb'):
        instance = self.model.objects.get(file_id=file_id)
        file = ContentFile(instance.blob)
        file.filename = instance.file_name
        file.mimetype = instance.mime_type
        return file

    def _save(self, name, content: File):
        blob = content.read()
        mime_type = getattr(content, 'content_type', 'text/plain')
        self.model.objects.create(
            file_name=name,
            blob=blob,
            file_size=content.size,
            mime_type=mime_type
        )
        return name

    def exists(self, name):
        return self.model.objects.filter(file_name=name).exists()


attachment_storage = AttachmentStorage()
方法名称 参数 返回值 用途
_open file_id, mode='rb' ContentFile 打开文件以供读取,根据给定的file_idAttachment模型中获取文件记录并返回ContentFile对象。
_save name, content: File 文件名 保存文件,将文件名和文件内容作为参数,创建Attachment模型记录并将文件信息保存到数据库。
exists name 布尔值 (TrueFalse) 检查文件是否存在,根据给定的文件名查询Attachment模型,返回True如果文件存在,否则返回False

这些方法共同组成了AttachmentStorage类,用于处理附件文件的存储和访问。_open 方法用于读取文件,_save 方法用于保存文件,而exists 方法用于检查文件是否存在。请注意,初始化方法__init__接受一个model参数。

定义视图

上传视图

py 复制代码
class AttachmentUploadView(APIView):
    permission_classes = (permissions.IsAdminUser,)

    def post(self, request, version):
        try:
            file = request.FILES['file']
        except MultiValueDictKeyError:
            raise ValidationError('参数错误')
        name = attachment_storage.save(file.name, file)
        attachment = get_object_or_404(Attachment, file_name=name)
        return JsonResponse({'download_url': attachment.get_url(request)}, status=status.HTTP_201_CREATED)
  • 作用:处理附件的上传操作。
  • 功能 :当接收到POST请求时,该视图尝试从请求中获取名为 'file' 的文件,然后使用自定义的存储后端 attachment_storage 来保存文件。接着,它在数据库中查找与文件名匹配的附件记录,并返回包含下载链接的 JSON 响应。这个视图的主要目的是允许用户上传附件,并提供上传后的附件的下载链接。

下载视图

py 复制代码
class AttachmentDownloadView(APIView):
    permission_classes = (permissions.IsAuthenticated,)

    def get(self, request, version, attachment_id=None):
        attachment = attachment_storage.open(attachment_id)
        response = HttpResponse(attachment, content_type=attachment.mimetype)
        response['Content-Disposition'] = 'attachment;filename={name}'.format(name=attachment.filename).encode('utf-8')
        return response
  • 作用:处理附件的下载操作。
  • 功能:当接收到GET请求时,该视图使用传递的 attachment_id 参数来打开相应的附件。然后,它创建一个包含附件内容的 HTTP 响应对象,设置响应的内容类型为附件的 MIME 类型,并设置响应头 Content-Disposition,指定附件的文件名。最后,它返回包含附件内容的 HTTP 响应。这个视图的主要目的是允许用户通过提供附件的唯一标识符来下载附件。

Content-Disposition 是一个HTTP响应头,它用于指示浏览器如何处理接收到的文件。具体来说,Content-Disposition 头的值告诉浏览器应该如何处理响应的内容,通常用于文件下载操作。

注册视图

py 复制代码
#!/usr/bin/python
# -*- coding: utf-8 -*-
from django.urls import re_path, path

from .views import AttachmentUploadView, AttachmentDownloadView

app_name = 'attachment'

urlpatterns = [
    re_path(r'upload', AttachmentUploadView.as_view(), name='upload'),
    path(r'download/<uuid:attachment_id>', AttachmentDownloadView.as_view(), name='download'),
]

使用swagger测试接口

上传

下载

结语

在开发Web应用程序时,文件上传和下载是常见的功能之一,但同时也需要特别关注安全性。通过合理的安全性配置,你可以保护应用程序和用户的数据免受潜在的威胁。在实际的项目中,我们可以增加一些重要的安全性措施,包括文件类型验证、文件大小限制、CSRF保护、存储路径安全性和其他关键措施,以确保文件上传和下载功能的安全性。

相关推荐
无为之士1 分钟前
Linux自动备份Mysql数据库
linux·数据库·mysql
小汤猿人类14 分钟前
open Feign 连接池(性能提升)
数据库
阳冬园35 分钟前
mysql数据库 主从同步
数据库·主从同步
Mr.132 小时前
数据库的三范式是什么?
数据库
Cachel wood2 小时前
python round四舍五入和decimal库精确四舍五入
java·linux·前端·数据库·vue.js·python·前端框架
Python之栈2 小时前
【无标题】
数据库·python·mysql
风_流沙2 小时前
java 对ElasticSearch数据库操作封装工具类(对你是否适用嘞)
java·数据库·elasticsearch
亽仒凣凣2 小时前
Windows安装Redis图文教程
数据库·windows·redis
亦世凡华、2 小时前
MySQL--》如何在MySQL中打造高效优化索引
数据库·经验分享·mysql·索引·性能分析
YashanDB2 小时前
【YashanDB知识库】Mybatis-Plus调用YashanDB怎么设置分页
数据库·yashandb·崖山数据库