Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SIPWU-1189: Fix 500 Error if file does not exist on server #12

Merged
merged 1 commit into from
Sep 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions drf_attachments/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,16 @@ class AttachmentAdmin(admin.ModelAdmin, AttachmentAdminMixin):
"creation_date",
)

def get_object(self, request, object_id, from_field=None):
obj = super().get_object(request, object_id, from_field)

# handling missing file (e.g. file may have been deleted on the server)
if not obj.file.storage.exists(obj.file.name):
self.message_user(request, "File not found", level="ERROR")
obj.file = None

return obj

@staticmethod
def content_object(obj):
entity = obj.content_object
Expand Down
12 changes: 4 additions & 8 deletions drf_attachments/models/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,11 +104,7 @@ def __str__(self):

@property
def is_image(self):
return (
"mime_type" in self.meta
and self.meta["mime_type"]
and self.meta["mime_type"].startswith("image")
)
return self.get_mime_type().startswith("image")

@property
def default_context(self):
Expand All @@ -126,13 +122,13 @@ def is_modified(self):
return self.creation_date != self.last_modification_date

def get_extension(self):
return self.meta.get("extension")
return self.meta.get("extension", "unkown")

def get_size(self):
return self.meta.get("size")
return self.meta.get("size", 0)

def get_mime_type(self):
return self.meta.get("mime_type")
return self.meta.get("mime_type", "unkown")

def save(self, *args, **kwargs):
# set computed values for direct and API access
Expand Down
27 changes: 19 additions & 8 deletions drf_attachments/rest/views.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
from django.http import FileResponse
from django.http import FileResponse, Http404
from django_filters.rest_framework import DjangoFilterBackend
from drf_attachments.storage import AttachmentFileStorage
from drf_attachments.models.models import Attachment
from drf_attachments.rest.renderers import FileDownloadRenderer
from drf_attachments.rest.serializers import AttachmentSerializer
from rest_framework import viewsets
from rest_framework.decorators import action, parser_classes
from rest_framework.filters import SearchFilter
Expand All @@ -8,10 +12,6 @@
from rest_framework.permissions import IsAuthenticated
from rest_framework.renderers import JSONRenderer

from drf_attachments.models.models import Attachment
from drf_attachments.rest.renderers import FileDownloadRenderer
from drf_attachments.rest.serializers import AttachmentSerializer

__all__ = [
"AttachmentViewSet",
]
Expand Down Expand Up @@ -41,10 +41,16 @@ def get_storage_path(self):
attachment = self.get_object()
meta = getattr(attachment.content_object, "AttachmentMeta", None)
storage_location = getattr(meta, "storage_location", None)
if storage_location:
return f"{storage_location}/{attachment.file.name}"

# Get custom storage location
if meta and storage_location:
storage = AttachmentFileStorage(location=meta.storage_location)
else:
return attachment.file.path
# Default storage value from FileField "file"
storage = attachment.file.storage

# Return the file path using the appropriate storage system
return storage.path(attachment.file.name)

@action(
detail=True,
Expand All @@ -62,6 +68,11 @@ def download(self, request, format=None, *args, **kwargs):
else:
download_file_name = f"attachment_{attachment.pk}{extension}"

# Check if file exists via storage due to custom storage locations
# without triggering SuspiciousFileOperation
if not attachment.file.storage.exists(attachment.file.name):
raise Http404()

return FileResponse(
open(storage_path, "rb"),
as_attachment=True,
Expand Down
5 changes: 3 additions & 2 deletions drf_attachments/storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ class AttachmentFileStorage(FileSystemStorage):
Attachments are served with a dedicated API route instead
"""

def __init__(self):
super().__init__(location=settings.PRIVATE_ROOT)
def __init__(self, *args, **kwargs):
kwargs.setdefault("location", settings.PRIVATE_ROOT)
super().__init__(*args, **kwargs)

def url(self, name):
from drf_attachments.models import Attachment
Expand Down
Loading