Foreword
Django
comes with a field named FileField
for processing file uploads. However, sometimes we need more control, such as defining the file’s storage path, file name, and file type. In this article, we will explore how to customize the Django attachment storage model.
Create an attachment application
python manage.py startapp attachment
Then, in the project’s settings.py
file, register the app into the INSTALLED_APPS
list, as follows:
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' ]
Create model
DefinitionAttachment
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('filename', max_length=200, unique=True) mime_type = models.CharField('MIME type', max_length=100) file_size = models.PositiveIntegerField('File length') blob = BlobField('File content') class Meta: verbose_name = 'attachment' verbose_name_plural = verbose_name def get_url(self, request): return reverse('attachment:download', request=request, kwargs={<!-- -->'attachment_id': self.file_id}) </code><img class="look-more-preCode contentImg-no-view" src="//i2.wp.com/csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreBlack. png" alt="" title="">
Field name | Type | Purpose |
---|---|---|
file_id | UUIDField | The unique identifier of the stored file |
file_name | CharField | The name of the stored file, that is, the original file name |
mime_type | CharField | MIME type of stored file |
file_size | PositiveIntegerField | The size of the stored file (in bytes) |
blob | Custom BlobField | The binary of the stored file Content, that is, the actual data of the file |
Apply changes to database
python manage.py makemigrations python manage.py migrate
Custom Django storage
Define storage class
#!/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): """Attachment 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() </code><img class="look-more-preCode contentImg-no-view" src="//i2.wp.com/csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreBlack. png" alt="" title="">
Method name | Parameters | Return value | Purpose |
---|---|---|---|
_open |
file_id, mode='rb' |
ContentFile |
Opens the file for reading, obtained from the Attachment model based on the given file_id File records and returns a ContentFile object. |
_save |
name, content: File |
File name | Save the file, take the file name and file content as parameters, create Attachment Model records and saves file information to the database. |
exists |
name |
Boolean value (True or False ) |
Checks whether the file exists, querying the Attachment model based on the given file name, returning True if the file exists, otherwise returning False . |
These methods together form the AttachmentStorage
class, which is used to handle the storage and access of attachment files. The _open
method is used to read the file, the _save
method is used to save the file, and the exists
method is used to check whether the file exists. Please note that the initialization method __init__
accepts a model
parameter.
Define views
Upload view
class AttachmentUploadView(APIView): permission_classes = (permissions.IsAdminUser,) def post(self, request, version): try: file = request.FILES['file'] except MultiValueDictKeyError: raise ValidationError('Parameter error') 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)
- Function: Process the uploading of attachments.
- Function: When a POST request is received, the view attempts to obtain a file named ‘file’ from the request and then saves the file using a custom storage backend
attachment_storage
. Next, it looks for an attachment record in the database that matches the file name and returns a JSON response containing the download link. The main purpose of this view is to allow users to upload attachments and provide download links for uploaded attachments.
Download view
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
- Function: Process the download operation of attachments.
- Function: When a GET request is received, this view uses the passed attachment_id parameter to open the corresponding attachment. It then creates an HTTP response object containing the attachment’s contents, sets the response’s content type to the attachment’s MIME type, and sets the response header Content-Disposition to specify the attachment’s file name. Finally, it returns an HTTP response containing the attachment content. The main purpose of this view is to allow users to download attachments by providing their unique identifier.
Content-Disposition
is an HTTP response header that instructs the browser how to handle received files. Specifically, the value of theContent-Disposition
header tells the browser how it should handle the content of the response, usually for file download operations.
Registration view
#!/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'), ]
Use swagger to test the interface
Upload
Download
Conclusion
When developing web applications, file uploading and downloading are one of the common functions, but security also requires special attention. With proper security configuration, you can protect applications and user data from potential threats. In actual projects, we can add some important security measures, including file type verification, file size limits, CSRF protection, storage path security and other key measures to ensure the security of file upload and download functions.